LINE WORKS × AWS Cognitoを使ったSAML,OpenID ConnectのSSO認証について
LINE WORKS × AWS Cognitoを使ったSAML,OpenID ConnectのSSO認証について
はじめに
初めまして。ファストドクター株式会社でソフトウェアエンジニアをしている今泉と申します。主にAWSを使ったインフラ構築とTypeScriptを使ったアプリケーション開発を行っています。
早速ですが、LINE WORKSとAmazon Cognito を、繋げたことありますか?
弊社でもCognitoベースのSSO認証を導入しています、とあるシステムでは LINE WORKSとつなげる必要があり、私が担当することになりました。Cognito/SSO もそこまで知識があったわけではない上に、LINE WORKS は簡単につながらず、特殊なことをしないといけなく、結構ハマってしまいました。
その時に、やったこと、注意点やノウハウを、ぜひ、みなさまに共有させてください。
だれに見て欲しいか
- SAMLのデバッグ方法を知りたい人
- LINE WORKSとAWS CognitoによるSSO認証を使ってアプリケーションにログイン機能を作りたい人
先にLINE WORKS × AWS CognitoのSSO認証の結論から
LINE WORKS(IdP側)とAWS Cognito(SP側)のSSO認証はできなくはないが、通常とは違うSSO認証でしか連携ができない。
実現する方法としては
- LINE WORKS(OAuth) × OAuth-OIDC-wrapper × AWS Cognito(OIDC)
のように、OAuthとOpenID Connect(OIDCと略します)を変換するWrapperを用意するしかAWS Cognitoでは動かないので
- セキュリティ要件を精査した上でwrapper構成を使う
- AWS Cognitoは使わずLINE WORKSのSAML要件にあう別のものにする
- LINE WORKSのSSO認証要件を諦める
この3つの選択肢をとる必要があります。
試したこと
LINE WORKS(SAML) × AWS Cognito(SAML)
LINE WORKS公式ドキュメントにはLINE WORKSをIdPとした場合のSSO認証はSAMLしかありません。
- LINE WORKSをIdPとしたSSOの公式ドキュメント
https://developers.worksmobile.com/jp/docs/sso-idp
IdP側でよくある機能として、SP側に連携するためのxmlファイルのメタデータドキュメントをダウンロードして、それをSPにアップロードしてIdP ⇄ SPのSAMLの通信を規定します。
しかし、LINE WORKS側にはメタデータドキュメントを提供する機能がなく、自分でSAMLの要件を確認しながらメタデータファイルをXMLで記述します。
メタデータファイルの一例
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2023-02-18T04:59:54Z" cacheDuration="PT1677128394S"entityID="xxxxxxxxxxxxxxx">
<md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>xxxxxxxxxx</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>xxxxxxxxxx</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="xxxxxxxxxxxxx"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>
そして出来上がったら実際に開発しているログイン画面から認証をしてみます。
その際、Google拡張機能の「SAML Chrome Panel」を開いておきます。この拡張機能はSAMLが使われている時には、SAML RequestとSAML Responseを可視化することができるので、SAMLのデバッグに便利です。
しかし、メタデータドキュメントを色々変えても、SAML Requestが下記2点においてLINE WORKS推奨のものにならずエラーになってしまいます。
① protcolbindingが表示されない
② Issuerに意図しないFormat指定がある
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
LINE WORKS推奨 SAML Request
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL="https://example.com/acs/vendor.com"
ID="fiokocckbjonklcjiepfejmoehpebebmholeoibp"
IssueInstant="2018-02-25T07:42:35Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
ProviderName="example.com"
Version="2.0">
<saml2:Issuer
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">SPIssuer</saml2:Issuer>
<saml2p:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameidformat:unspecified"/>
</saml2p:AuthnRequest>
AWS Cognitoにメタデータを登録した結果のSAML Request
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL="https://auth.dev.iwith.xyz/saml2/idpresponse"
Destination="https://auth.worksmobile.com/saml2/idp/works-506217"
ID="_3e9f7504-14d1-42df-a130-172624d6935e"
IssueInstant="2023-02-08T06:08:26.814Z" Version="2.0">
<saml2:Issuer
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">urn:amazon:cognito:sp:xxxxxxxxxxxxxxx</saml2:Issuer>
</saml2p:AuthnRequest>
SAML公式ドキュメントであるSAML Wikiを見ながらメタデータを色々調整したがうまくいきませんでした。
次に、IdPやSPをDockerでKeycloakを立てて実際に処理を見立ててデバッグしていきました。
余談ですが、KeycloakはOIDC,SAML,OAuthなどの認証・認可の標準仕様を押さえているOSSで、認証認可の開発をする方には是非抑えていただきたいOSSです。
詳しいことを書くとそれだけで一つの記事になりますので、詳細は下記をご覧ください。
しかしそれでもデバックは進まず最終的にAWSのサポート窓口に確認して
AWS Cognitoの仕様上、LINE WORKSの推奨のSAML Requestは送れないと結論がでました。
LINE WORKS(OAuth) × OAuth-OIDC-wrapper × AWS Cognito(OIDC)
調べている中でOAuth × OIDCをwrapperで疎通させる方法があり、試したところ認証はうまくいきました。
概要
- LINE WORKS側をOAuth2.0のAccount認証(OAuth)
- AWS Cognito側はOIDC
- 上記はそのままだとプロトコルが違うので、OAuth ⇄ OIDC を変換するwrapperをAWS Lambdaで立てる
シーケンス図
黒線はAWS Cognitoの処理部分、赤線はそれ以外で分けて表示しています。
やったこと
- github-cognito-openid-wrapper
https://github.com/TimothyJones/github-cognito-openid-wrapper
元々GitHubとAWS CognitoのOIDC ⇄ OAuthのラッパーでしたが、これをLINE WORKS用に修正し、AWS SAMでLambdaをデプロイしました。
Lambdaが4つできますので、API GatewayのエンドポイントをAWS CognitoのOIDCの設定で
- 承認エンドポイント
- トークンエンドポイント
- UserInfoエンドポイント
- Jwks_uriエンドポイント
に、それぞれ対応するエンドポイントを入れて、許可されたスコープを
- openid
- user
に設定してあげます。
LINE WORKS側のOAuthについてはUser Account認証を使い実装します。
自身のLINE WORKSプロフィールの取得方法は公式ドキュメントにはあまり記載がないですが、メールアドレスがあれば
GET https://www.worksapis.com/v1.0/users/me
としてあげると個別のLINE WORKSプロフィールの情報が取得できます。
AWS CognitoのJWT検証は、AWS公式ライブラリのaws-jwt-verifyを使って検証をするためのLambdaを別途で立て、ログインが必要なページではSessionStorageに保存されているJWTの検証を行い、問題なければ表示する形にしてます。
aws-jwt-verifyのLambdaコードサンプル例
import { CognitoJwtVerifier } from "aws-jwt-verify";
async function verifyJwt(jwt) {
const verifier = CognitoJwtVerifier.create({
userPoolId: "xxxxxxxxxxxxxx",
tokenUse: "id", // アクセストークンの時は access
clientId: "xxxxxxxxxxxxxx",
});
try {
const payload = await verifier.verify(jwt);
console.log("Token is valid. Payload: ", payload);
return payload;
} catch (err) {
console.error(err);
console.log("Token not valid!");
return { message: "Token not valid" };
}
}
export const handler = async (event) => {
console.info("received:", event);
const jwt = event.headers.authorization.split(" ")[1];
const payload = await verifyJwt(jwt);
const response = {
statusCode: 200,
body: JSON.stringify(payload),
};
console.info(
`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`
);
return response;
};
まとめ
今回はLINE WORKSとAWS CognitoをWrapperでOIDCを使ってSSOを連携しました。
IdPやSPがSAMLやOIDCに対応しているとドキュメントに記載されているから必ず実装できるわけでなく、個々のIdPやSPのサービスの要件をちゃんと確認して実際に実装できるかどうかまで確認をした方が余計な時間をかけなくて済むので、皆さんも是非この点を覚えていただければと思います。ここまで閲覧ありがとうございました。
参考URL
-
github-cognito-openid-wrapper
https://github.com/TimothyJones/github-cognito-openid-wrapper -
aws-jwt-verify
https://github.com/awslabs/aws-jwt-verify -
LINE WORKSをIdPとしたSSOの公式ドキュメント
https://developers.worksmobile.com/jp/docs/sso-idp -
LINE WORKSでのUser Account認証(OAuth)の公式ドキュメント
https://developers.worksmobile.com/jp/docs/auth-oauth -
Key Cloak Getting Start
https://www.keycloak.org/getting-started/getting-started-docker
免責
こちらの記事を元にSSO認証を実装される際にはセキュリティ観点等を踏まえて自己責任でお願いいたします。
Discussion