前回の記事の続きです。今回は、実際に実装して検証しました。
実装方針
MSのサイトに、node.jsとASP.NETのサンプルはすでにあるので、今回は別の言語で実装することにします。
今回は最近愛してやまない、PHPのLaravelで実装しました。Laravelサイコー。
なお、基本的には以下のドキュメントに記載の内容の後追いになります。こっちと自分の記事を読みながら検証するのがベスト。
https://docs.microsoft.com/ja-jp/office/dev/add-ins/develop/sso-in-office-add-ins
https://docs.microsoft.com/ja-jp/office/dev/add-ins/develop/register-sso-add-in-aad-v2
準備・設定
前提
-
今回構築する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] -
「アプリの登録」より、「新規登録」をクリックします。
-
以下のように入力し、「登録」をクリックします。
-
アプリケーション (クライアント) IDとディレクトリ (テナント) IDをコピーしておきます。(あとで使用します)
-
「証明書とシークレット」をクリックし、「新しいクライアント シークレット」をクリックして、クライアントシークレットをコピーします。(こちらも後で使用します)
- 「API の公開」→「設定」をクリックし、値に以下を入力します。
api://(Webサイトのドメイン)/(先ほどコピしたアプリケーションID)
例: api://127.0.0.1:8000/f4255842-2ac6-4b01-b8a6-aaaaaaaaaaaa
- 「Scopeの追加」をクリックし、以下のように入力します。
※スコープ名に「access_as_user」、同意できるのは誰ですかを「管理者とユーザー」と記入し、後は適当に
- 「承認済みのクライアント アプリケーション」下の「クライアント アプリケーションの追加」をクリックし、
「クライアントID」に以下の3つを入力し、「承認済みのスコープ」チェックを行い、「アプリケーションの追加」をクリックします。
(Office系の設定値らしいです)
d3590ed6-52b3-4102-aeff-aad2292ab01c
57fb890c-0dab-4253-a5e0-7188c88b2bb4
bc59ab01-8403-45c6-8796-ac3ef710b3e3
- 「APIのアクセス許可」をクリックし、[Microsoft Graph] を選択してから [委任されたアクセス許可] を選択します。
その後、以下にチェックを行い、「アクセス許可の更新」をクリックします。
offline_access
openid
profile
最後に、「既定のディレクトリ に管理者の同意を与えます」をクリックします。
以上で、Azure AD v2側の設定は完了です。
Laravel開発
Laravelの開発を行っていきます。
※composerをインストールしている前提です。composerの説明は割愛します。
-
インストールするフォルダで、コマンドプロンプトなどで、以下のコマンドを実行します。
実行後、officejs_sso_laravelフォルダにcdします。
composer create-project "laravel/laravel=5.6.*" officejs_sso_laravel -
以下のリポジトリより、phpファイルをダウンロードします。
https://github.com/hirossyi73/office-js-sso-laravel
該当ファイルを、Laravelディレクトリに上書きます。
- .envファイルに、以下の値を記入します。
CLIENT_ID=(コピーしたアプリケーションID)
CLIENT_SECRET="(コピーしたクライアントシークレット。ダブルクオーテーションで囲むことを推奨)"
SCOPE="offline_access openid profile"
TENANT=(コピーしたテナントID)
- 以下のコマンドを実行します。
php artisan serve
これで、以下のURLでサーバーが起動します。
http://127.0.0.1:8000
Officeアドイン設定
- githubより、マニフェストファイルをダウンロードします。
https://github.com/hirossyi73/office-js-sso-laravel/blob/master/Office-Add-in-Laravel.xml
- ダウンロードしたマニフェストファイルを開き、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を開き、アドインを起動します。
- 正常終了すると、このようにトークンが取得されます。
- このうち、
- アドイントークン: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を使用したサンプルは完了します。
今後実装を検討される方は、是非参考にしてみてください!
コメントを残す