本文へ移動
ドキュメント · 任意のOIDCライブラリ

クイックスタート:任意のOIDCライブラリ

Usherは標準的なOpenID Connectプロバイダーです。お使いの言語に認定済みのOIDCクライアントがあれば、必要なものはすでにそろっています。環境のディスカバリドキュメントを指定して、PKCEを使う認可コードフローを使ってください。

設定

設定値
発行者https://<handle>.usherid.com
ディスカバリURLhttps://<handle>.usherid.com/.well-known/openid-configuration
フロー認可コード(response_type=code)
PKCE必須、code_challenge_method=S256
スコープopenid profile email offline_access
クライアント認証token_endpoint_auth_methods_supportedに記載されている方式

環境のハンドルは、Usher Consoleで発行者の横に表示されています。開発環境にはwise-otter-42のようなランダムなハンドルが付き、本番環境には自分で選んだハンドルが付きます。本番環境は、login.acme.comのような独自ドメインに置くこともできます。その場合は、CNAMEレコードでそのドメインをUsherに向けた時点で、それがその環境の発行者になります。

ディスカバリドキュメントを読む

クライアントに必要なエンドポイントと鍵は、すべてここに記載されています。URLをコードにコピーするのではなく、ライブラリに読み込ませてください。

ターミナル
curl -s https://acme.usherid.com/.well-known/openid-configuration | jq
レスポンス(抜粋)
{
  "issuer": "https://acme.usherid.com",
  "authorization_endpoint": "https://acme.usherid.com/api/auth/oauth2/authorize",
  "token_endpoint": "https://acme.usherid.com/api/auth/oauth2/token",
  "userinfo_endpoint": "https://acme.usherid.com/api/auth/oauth2/userinfo",
  "jwks_uri": "https://acme.usherid.com/api/auth/jwks",
  "introspection_endpoint": "https://acme.usherid.com/api/auth/oauth2/introspect",
  "revocation_endpoint": "https://acme.usherid.com/api/auth/oauth2/revoke",
  "end_session_endpoint": "https://acme.usherid.com/api/auth/oauth2/end-session",
  "scopes_supported": ["openid", "profile", "email", "offline_access"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
  "code_challenge_methods_supported": ["S256"]
}

フロー

ユーザーを認可エンドポイントへ送る

ランダムなPKCEベリファイアを作り、ランダムなstateとともに、このブラウザーのセッションだけがアクセスできる場所に保存します(有効期間の短いhttpOnlyのCookieが適しています)。そのうえで、response_type=code、client_idとredirect_uri、上記のスコープ、state、そしてベリファイアのS256のcode_challengeを付けてリダイレクトします。

ユーザーにサインインしてもらう

ここからはLobbyが引き受けます。アプリ名を表示し、環境で許可されたサインイン方法を提示し、初回は同意を求めます。

コールバックを確認する

Usherは、codeとstateを付けてredirect_uriにリダイレクトします。stateが保存したものと一致しない限り、レスポンスを拒否してください。コードは一回限りで、有効期間は5分です。

コードをトークンと交換する

サーバーから、コードとPKCEベリファイアをトークンエンドポイントにPOSTします。

トークンリクエスト
curl -s -X POST https://acme.usherid.com/api/auth/oauth2/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri=https://app.acme.example/callback \
-d code_verifier="$CODE_VERIFIER"

IDトークンを検証する

jwks_uriの鍵で署名を確認し、issが発行者であること、audがクライアントIDであること、expが未来の時刻であることを確認します。これはライブラリが代わりに行いますが、その機能が無効になっていないことを確かめてください。

Node.jsの場合

openid-clientは、検証も含めて上記のすべてを行います。

openid-client
import * as client from 'openid-client'

// discovery: every endpoint and key comes from the issuer
const config = await client.discovery(
  new URL('https://acme.usherid.com'),
  process.env.USHER_CLIENT_ID!,
  process.env.USHER_CLIENT_SECRET!,
)

// 1. send them to the Lobby, with PKCE (S256) and a state to check later
const codeVerifier = client.randomPKCECodeVerifier()
const state = client.randomState()
const url = client.buildAuthorizationUrl(config, {
  redirect_uri: 'https://app.acme.example/callback',
  scope: 'openid profile email offline_access',
  code_challenge: await client.calculatePKCECodeChallenge(codeVerifier),
  code_challenge_method: 'S256',
  state,
})
// keep codeVerifier and state in a short-lived httpOnly cookie, then redirect to url

// 2. back at /callback: swap the code for tokens
const tokens = await client.authorizationCodeGrant(config, new URL(request.url), {
  pkceCodeVerifier: codeVerifier,
  expectedState: state,
})
const { sub, email, name } = tokens.claims()!

ほかの言語の場合

次の広く使われているライブラリは、どれも標準のOpenID Connectを話し、Usher固有の処理なしでUsherと連携できます。

言語ライブラリ
PythonAuthlib
Gogolang.org/x/oauth2と組み合わせたcoreos/go-oidc
JavaSpring Security OAuth 2.0 Client
.NETMicrosoft.AspNetCore.Authentication.OpenIdConnect
Rubyomniauth_openid_connect
PHPjumbojett/openid-connect-php

サインイン状態を保つ

offline_accessスコープを使うと、トークンレスポンスにリフレッシュトークンが含まれます。古いアクセストークンの期限が切れたら、grant_type=refresh_tokenでリフレッシュトークンを使い、新しいアクセストークンを取得します。リフレッシュトークンはローテーションされます。使うたびに新しいトークンが返され、古いトークンは使えなくなります。

サインアウト

まず自分のアプリのセッションを消去し、次にpost_logout_redirect_uri(あればid_token_hintも)を付けてend_session_endpointへリダイレクトします。こうすることで、Lobbyもそのユーザーのことを忘れます。