Office Devlopのいろは

Office系の技術を中心に、コアでニッチな話題を繰り広げます。

【Officeアドイン】OfficeアドインでSSOを有効化 実装編[Laravel]

office.kajitori.co.jp 前回の記事の続きです。今回は、実際に実装して検証しました。

実装方針

MSのサイトに、node.jsASP.NETのサンプルはすでにあるので、今回は別の言語で実装することにします。
今回は最近愛してやまない、PHPLaravelで実装しました。Laravelサイコー。

なお、基本的には以下のドキュメントに記載の内容の後追いになります。こっちと自分の記事を読みながら検証するのがベスト。
docs.microsoft.com

docs.microsoft.com

準備・設定

前提

  • 今回構築するLaravelサイトの情報は以下になります。
    http://127.0.0.1:8000
    ※実際に公開する場合は、「127.0.0.1:8000」を修正してください
    ※また、この記事を書き始めた当初、「localhost:9000」と思い、キャプチャを作成し始めてしまいました。そのため、キャプチャ内に「localhost:9000」があった場合、「127.0.0.1:8000」に修正をお願いします。

  • Laravel5.6で構築しております。

Office365インストール

Office365のクライアント版を、Office365のサイトからインストールします。

Azure AD v2設定

  • 以下のAzureポータルページにアクセスし、Office365のアカウントでサインインします。
    https://go.microsoft.com/fwlink/?linkid=2083908

  • 「アプリの登録」より、「新規登録」をクリックします。 f:id:hsatou_kajitori:20190525213050p:plain

  • 以下のように入力し、「登録」をクリックします。
    f:id:hsatou_kajitori:20190525213149p:plain

  • アプリケーション (クライアント) IDとディレクトリ (テナント) IDをコピーしておきます。(あとで使用します)
    f:id:hsatou_kajitori:20190525213414p:plain

  • 「証明書とシークレット」をクリックし、「新しいクライアント シークレット」をクリックして、クライアントシークレットをコピーします。(こちらも後で使用します)
    f:id:hsatou_kajitori:20190525213534p:plain

f:id:hsatou_kajitori:20190525213643p:plain

f:id:hsatou_kajitori:20190525213747p:plain

  • API の公開」→「設定」をクリックし、値に以下を入力します。
    api://(Webサイトのドメイン)/(先ほどコピしたアプリケーションID)
    例: api://127.0.0.1:8000/f4255842-2ac6-4b01-b8a6-aaaaaaaaaaaa

f:id:hsatou_kajitori:20190525221840p:plain

  • 「Scopeの追加」をクリックし、以下のように入力します。
    ※スコープ名に「access_as_user」、同意できるのは誰ですかを「管理者とユーザー」と記入し、後は適当に

f:id:hsatou_kajitori:20190525214443p:plain

  • 「承認済みのクライアント アプリケーション」下の「クライアント アプリケーションの追加」をクリックし、
    「クライアントID」に以下の3つを入力し、「承認済みのスコープ」チェックを行い、「アプリケーションの追加」をクリックします。
    (Office系の設定値らしいです)
    d3590ed6-52b3-4102-aeff-aad2292ab01c
    57fb890c-0dab-4253-a5e0-7188c88b2bb4
    bc59ab01-8403-45c6-8796-ac3ef710b3e3

f:id:hsatou_kajitori:20190525215007p:plain

f:id:hsatou_kajitori:20190525215115p:plain

  • APIのアクセス許可」をクリックし、[Microsoft Graph] を選択してから [委任されたアクセス許可] を選択します。
    その後、以下にチェックを行い、「アクセス許可の更新」をクリックします。
    offline_access
    openid
    profile

f:id:hsatou_kajitori:20190525215633p:plain

f:id:hsatou_kajitori:20190525215857p:plain

最後に、「既定のディレクトリ に管理者の同意を与えます」をクリックします。

f:id:hsatou_kajitori:20190525220019p:plain

以上で、Azure AD v2側の設定は完了です。

Laravel開発

Laravelの開発を行っていきます。
※composerをインストールしている前提です。composerの説明は割愛します。

該当ファイルを、Laravelディレクトリに上書きます。

f:id:hsatou_kajitori:20190525221236p:plain

  • .envファイルに、以下の値を記入します。
    CLIENT_ID=(コピーしたアプリケーションID)
    CLIENT_SECRET="(コピーしたクライアントシークレット。ダブルクオーテーションで囲むことを推奨)"
    SCOPE="offline_access openid profile"
    TENANT=(コピーしたテナントID)

f:id:hsatou_kajitori:20190528091147p:plain

  • 以下のコマンドを実行します。
    php artisan serve

これで、以下のURLでサーバーが起動します。
http://127.0.0.1:8000

Officeアドイン設定

office-js-sso-laravel/Office-Add-in-Laravel.xml at master · hirossyi73/office-js-sso-laravel · GitHub

  • ダウンロードしたマニフェストファイルを開き、135行目以降のWebApplicationInfo以下の内容を修正します。
    <WebApplicationInfo>
      <Id>(アプリケーションID)</Id>
      <Resource>api://(ドメイン)/(アプリケーションID)</Resource>
        <Scopes>
            <Scope>openid</Scope>
            <Scope>offline_access</Scope>
            <Scope>profile</Scope>
        </Scopes>
    </WebApplicationInfo>
  • マニフェストファイルを、Officeのセキュリティ設定で「カタログ URL」に設定したパスに配置します。

  • PowerPointを開き、アドインを起動します。

f:id:hsatou_kajitori:20190528143501p:plain

  • 正常終了すると、このようにトークンが取得されます。

f:id:hsatou_kajitori:20190528143602p:plain

  • このうち、
    • アドイントークン:OfficeのSSOから取得したトークンです。
    • ユーザー名:アドイントークンから取得したログインユーザー名です。
    • Eメール:アドイントークンから取得したメールアドレスです。
    • Graphアクセストークン:アドイントークンを使用し、PHP(Laravel)側で取得したMicrosoft Graphトークンです。もちろん、このトークンをMicrosoft Graphに使用すれば、情報を取得できます。
    • Graphリフレッシュトークン:アドイントークンを使用し、PHP(Laravel)側で取得したMicrosoft Graphのリフレッシュトークンです。

解説

ソース単位でご説明します。

アドイントークン取得

以下の内容で、アドイントークンを取得しています。

//  app.js
(function(){
    // 初期化処理
    Office.onReady(function(info) {
        if(!Office.context.auth.getAccessTokenAsync){
            log('Office.context.auth.getAccessTokenAsync not supported');
            return;
        }

        // 認証実行
        Office.context.auth.getAccessTokenAsync(function (result) {
            if (result.status === "succeeded") {
                // Use this token to call Web API
                var ssoToken = result.value;
                $('#addin_token').val(ssoToken);

                // decode user info
                var decoded = jwt_decode(ssoToken);

                $('#username').val(decoded.name);
                $('#email').val(decoded.preferred_username);

                getGraphToken(ssoToken);
            } else {
                if (result.error.code === 13003) {
                    // SSO is not supported for domain user accounts, only
                    // work or school (Office 365) or Microsoft Account IDs.
                } else {
                    // Handle error
                }
                
                log('error ' + result.error.code);
            }
        });
    });
})();

ポイントは「Office.context.auth.getAccessTokenAsync(function (result) {」で、この関数で、PowerPointのアプリケーションからSSOで、アドインのトークンを取得しています。 トークンはJWT形式なので、「//decode user info」の部分で内容を解析し、名前とメールアドレスを取得しています。

Microsoft Graphトークン取得

以下の内容で、Microsoft Graphトークンを取得しています。

//  app.js

/**
 * Microsoft Graphのトークンをサーバーサイドから取得
 * @param string apptoken アドイントークン
 */
function getGraphToken(apptoken){
    var CSRF_TOKEN = $('meta[name="csrf-token"]').attr('content');
    
    $.ajax({
        url:'./graphtoken',
        type:'POST',
        data:{
            'apptoken':apptoken,
            '_token' : CSRF_TOKEN,
        }
    })
    // Ajaxリクエストが成功した時発動
    .done(function(data){
        $('#graph_token').val(data.access_token);
        $('#graph_refresh_token').val(data.refresh_token);
    })
    // Ajaxリクエストが失敗した時発動
    .fail(function(data){
        log('Graph token error : ' + JSON.stringify(data));
    })
    // Ajaxリクエストが成功・失敗どちらでも発動
    .always(function(data){
    });
}
// IndexController.php
    /**
     * Graphのトークン取得
     *
     * @return void
     */
    public function graphtoken(){
        $apptoken = request()->get('apptoken');
        
        // ホントはここで検証を行う

        // Graphのアクセストークンを代理フローで取得
        // https://docs.microsoft.com/ja-jp/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow
        $client = new \GuzzleHttp\Client;

        $res = $client->request(
            'POST',
            'https://login.microsoftonline.com/common/oauth2/v2.0/token',
            [
                'form_params' => [
                    'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                    'client_id' => env('CLIENT_ID'),
                    'client_secret' => env('CLIENT_SECRET'),
                    'assertion' => $apptoken,
                    'scope' => env('SCOPE'),
                    'requested_token_use' => 'on_behalf_of',
                ]
            ]
        );

        $list = json_decode($res->getBody()->getContents(), true);
        return $list;
    }

検証をサボってますが、本番環境では検証、ちゃんとやってくださいね。
この内容によって、代理フローを使用し、Microsoft Graphトークンを取得するわけです。

まとめ

長くなってしまいましたが、以上でSSOを使用したサンプルは完了します。
今後実装を検討される方は、是非参考にしてみてください!