認証と認可のまとめ
アクセストークン:認可(リソースへのアクセスコントロール=あるリソースへの権限(readやwriteなど)を持っているかどうか確認すること)
IDトークン:認証(その人が誰かを確認すること)
ID トークンに含まれる情報により、ユーザーが主張する通りの人物であることをクライアントで確認できます。 ID トークンは、サードパーティのアプリケーションで理解されることを意図しています。 ID トークンは承認目的では使用すべきではありません。 承認には、アクセス トークンを使用します。
Microsoft ID プラットフォームでは、OAuth 2.0 の仕様で説明されているように、OAuth 2.0 の暗黙的な許可フローがサポートされています。
OAuth2 の暗黙的な許可の適切なシナリオ
暗黙的な許可は、サード パーティの Cookie がなくてもアプリケーションに影響を与えないサインイン フローの最初の対話的な部分でしか信頼できません。 つまり、アプリケーションが承認エンドポイントからのトークンとコードを要求するハイブリッド フローの一部としてのみこの許可を使用すべきです。 そうすることで、アプリケーションは更新トークンと交換可能なコードを受け取ることができるため、アプリケーションのログイン セッションは時間が経過しても有効なままになります。
"暗黙的フロー"の説
- アクセストークンに付与するクレームをRBACで
↑ id_token と アクセス token の違い
id token を保存するとかそのへん
scope の実装も可能であるっぽい
OpenID Connect は、OAuth 2.0 の "認可" プロトコルを "認証" プロトコルとして使用できるように拡張したものです。 この認証プロトコルでは、シングル サインオンを実行できます。 ここでは、クライアントがユーザーの ID を検証したり、ユーザーに関する基本的なプロファイル情報を取得したりできるようにする "ID トークン" の概念が導入されています。
OAuth 2.0 の Scope にアクセスできるリソースやアクセス権の範囲を記載する具体例を教えてください
ChatGPT
OAuth 2.0のScopeは、クライアントアプリケーションがリソースサーバーに対して要求するアクセス権の範囲を示すために使用されます。以下に具体的な例を示します。
プロフィール情報へのアクセス:
Scope: profile
説明: ユーザーの基本的なプロフィール情報(氏名、メールアドレス、プロフィール写真など)にアクセスする権限を示します。
電子メールアドレスへのアクセス:
Scope: email
説明: ユーザーの登録されたメールアドレスにアクセスする権限を示します。
連絡先情報へのアクセス:
Scope: contacts.read
説明: ユーザーの連絡先情報(アドレス帳)への読み取りアクセス権を示します。
カレンダーへのアクセス:
Scope: calendar.read, calendar.write
説明: ユーザーのカレンダー情報への読み取りおよび書き込みアクセス権を示します。
ソーシャルメディアの投稿へのアクセス:
Scope: socialmedia.posts
説明: ユーザーのソーシャルメディアアカウントでの投稿へのアクセス権を示します。
ファイルへのアクセス:
Scope: files.read, files.write
説明: ユーザーのファイルへの読み取りおよび書き込みアクセス権を示します。
これらは一般的な例であり、実際のアプリケーションに応じて独自のScopeを定義することも可能です。リソースサーバーは、クライアントアプリケーションが要求するScopeに基づいて、ユーザーのアクセス権を制御します。
GCP の IDaas もやろうと思えば(実装あり)できるみたい https://medium.com/google-cloud/adding-custom-roles-to-jwt-on-login-with-google-identity-platform-476cd15301d9
access token の claim に permission を載せるけれども、RBAC で管理する手法ってどんなものがあるんでしょうか。
Auth0 だと機能として RBAC はあるみたいなのですが、他の IDaas だとどのように実現するのでしょうか。
azure ad b2c の場合 scope はどうやって変更するのか
OAuth と OpenID Connect:このプラットフォームでは、OAuth が承認に使用され、OpenID Connect (OIDC) が認証に使用されます。 OpenID Connect は OAuth 2.0 に基づいて構築されているため、これら 2 つの間では用語とフローが似ています。 さらに、(OpenID Connect を使用して) ユーザーを認証することと、(OAuth 2.0 を使用して) ユーザーが所有する保護されたリソースにアクセスするための承認を得ることを、1 回の要求で行うこともできます。
アクセストークンを要求する方法が書いてあった
承認コード付与フロー - シングルページ アプリ (SPA)、モバイル アプリ、ネイティブ (デスクトップ) アプリケーション
基礎
SPAでアクセストークンを使用する流れ
これで良いらしい
AzureAD で RBAC な アクセス制御は難しそうだったので、Auth0で考える。
AzureAD で RBAC な アクセス制御は難しそうだったので、Auth0で考える。
なかなかのお値段だった
Auth0
- Open API は無し
token の検証
user 情報の取得
- 手段1
- access token を使って user 情報を取得する
- 手段2
- id token にメールアドレスを含めるようにする - 手段3
- access token にメールアドレスを含めるようにする
jwt がわかりやすい
これを元に実装する
SPA のサポートには、OAuth 2.0 認証コード フロー (PKCE あり) の方法が推奨されます。
OAuth 2.0 認証コード フロー (PKCE あり) = OAuth 2.0 承認コード フロー
- これが一番わかりやすい
- PKCEありの方の認証
認可まで進んだ
msal の使い方はこのあたりに記述がある
- MSAL Python is a token acquisition and caching library, and not a token validation library
リフレッシュトークンがわかりやすい
acess token の検証でやるべきこと
AADB2Cでは メールを自動入力できる。
AzureAD の画面カスタマイズ (上手く動かない)
注意:
セルフサービス パスワード リセット にチェックを付けておかない、画面でforgotpasswordを押した際にエラーになる。
https://<アプリ名>.b2clogin.com/<アプリ名>.onmicrosoft.com/B2C_1_sign_in/api/CombinedSigninAndSignup/confirme
の中に Authorization Code が入っている。
SPA
例外の取り回しが良くないけれど、なんとか実装できる感じ。
try:
return verify_jwt(
token=token,
valid_audiences=[az_ad_auth_config.app_id],
issuer=az_ad_auth_config.ad_issuer,
jwks_uri=az_ad_auth_config.jwks_uri,
verify=True,
)
except InvalidAuthorizationToken as e:
logger.error("Token validation error: %s", e, exc_info=True)
if "ExpiredSignatureError" in str(e):
raise HTTPException(status_code=401, detail="Token is expired")
実装途中だけど、自前で作るならこんな感じ
import os
import requests
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2AuthorizationCodeBearer
import jwt
from jwt.algorithms import RSAAlgorithm
from pydantic import BaseModel
import logging
logger = logging.getLogger(__name__)
# 環境変数から設定を読み込む
TENANT_ID = os.getenv("AZURE_TENANT_ID")
CLIENT_ID = os.getenv("AZURE_CLIENT_ID")
APP_NAME = os.getenv("APP_NAME")
JWKS_URL = f"https://{APP_NAME}.b2clogin.com/{APP_NAME}.onmicrosoft.com/b2c_1_sign_in/discovery/v2.0/keys"
ISSUER = f"https://{APP_NAME}.b2clogin.com/{TENANT_ID}/v2.0/"
oauth2_scheme = OAuth2AuthorizationCodeBearer(
authorizationUrl=f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/authorize",
tokenUrl=f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
)
class TokenPayload(BaseModel):
sub: str
scp: str
def get_public_key(token):
header = jwt.get_unverified_header(token)
kid = header["kid"]
logger.debug(f"{kid=}")
response = requests.get(JWKS_URL)
jwks = response.json()
for key in jwks["keys"]:
logger.debug(f"{key['kid']=}")
if key["kid"] == kid:
return RSAAlgorithm.from_jwk(key)
raise HTTPException(status_code=401, detail="Public key not found")
def validate_token(token: str = Depends(oauth2_scheme)) -> TokenPayload:
public_key = get_public_key(token)
try:
payload = jwt.decode(token, public_key, algorithms=["RS256"], audience=CLIENT_ID, issuer=ISSUER)
return TokenPayload(**payload)
except Exception as e:
logger.error("Token validation error: %s", e, exc_info=True)
raise HTTPException(status_code=401, detail="Invalid token")
def require_scope(required_scope: str):
def decorator(payload: TokenPayload = Depends(validate_token)):
if required_scope not in payload.scp.split():
raise HTTPException(status_code=403, detail="Forbidden")
return payload
return decorator
Azure AD B2C OpenID Connect メタデータ ドキュメント を実行して、
ISSUER や JWKS_URI を取得する
MS Graph
この例でuser作成できた。
MS Graph
カスタムポリシー
- チュートリアル
- ポータル含めて実装されているやつ
- Audit Log を出力するやつ
- Aplication Insight で監視する
- カスタムポリシーに関する解説
- https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/custom-policy-overview
- https://qiita.com/skmk/items/52eee28517756be61b14
- https://qiita.com/skmk/items/3efdb97833a1ca6e3008
- https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policies-series-hello-world
- https://blog.azure.moe/2019/11/22/冴えないcustom-policyの育て方/?msclkid=41a93003cee011ecb8ae8b050d50f7dc
- ベストプラクティス
Microsoft公式サンプルコード
-
AD B2C カスタムポリシー Starter Pack
- 基本的な認証フローのカスタムポリシーのサンプルコード
-
Azure Active Directory B2C: Custom CIAM User Journeys
- パスワードの強制リセットや利用規約などといった様々なケースに対応したカスタムポリシーのサンプルコード
OpenID Connect Provider を AADB2Cで設定する例
SSOのディシジョンツリー
ReBAC
複数のテナントにログインできる事例