Auth0 では、次を使用してアプリに PKCE を使用する認可コードフローを簡単に実装できます。
このチュートリアルを始める前に:
code verifier を作成 :
トークンをリクエストするために Auth0 に送信する code_verifier を生成します。
code challenge を作成 :
Auth0 に送信して authorization_code をリクエストするための code_challenge を、code_verifier から生成します。
ユーザーを認可 :
ユーザーの認可を取得し、authorization_code とともにアプリへリダイレクトします。
トークンをリクエスト :
authorization_code と code_verifier を使ってトークンを取得します。
API を呼び出す :
取得したアクセストークンを使用して API を呼び出します。
トークンを更新 :
既存のトークンの有効期限が切れたら、リフレッシュトークンを使用して新しいトークンをリクエストします。
任意: サンプルのユースケースを確認 。
code_verifier を作成します。これは、後でトークンをリクエストするために Auth0 に送信する、暗号学的にランダムな Base64 エンコード済みのキーです。
// 依存関係: Node.js crypto モジュール
// https://nodejs.org/api/crypto.html#crypto_crypto
function base64URLEncode ( str ) {
return str . toString ( 'base64' )
. replace ( / \+ / g , '-' )
. replace ( / \/ / g , '_' )
. replace ( /=/ g , '' );
}
var verifier = base64URLEncode ( crypto . randomBytes ( 32 ));
Java の例
// 依存関係: Apache Commons Codec
// https://commons.apache.org/proper/commons-codec/
// Base64クラスをインポートします。
// import org.apache.commons.codec.binary.Base64;
SecureRandom sr = new SecureRandom ();
byte [] code = new byte [ 32 ];
sr . nextBytes (code);
String verifier = Base64 . getUrlEncoder (). withoutPadding (). encodeToString (code);
// https://developer.android.com/reference/android/util/Base64 を参照
// Base64クラスをインポートする
// import android.util.Base64;
SecureRandom sr = new SecureRandom ();
byte[] code = new byte[ 32 ];
sr. nextBytes (code);
String verifier = Base64. encodeToString (code, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
var buffer = [ UInt8 ]( repeating : 0 , count : 32 )
_ = SecRandomCopyBytes (kSecRandomDefault, buffer. count , & buffer)
let verifier = Data (buffer). base64EncodedString ()
. replacingOccurrences ( of : "+" , with : "-" )
. replacingOccurrences ( of : "/" , with : "_" )
. replacingOccurrences ( of : "=" , with : "" )
NSMutableData * data = [ NSMutableData dataWithLength: 32 ];
int result __attribute__ ((unused)) = SecRandomCopyBytes ( kSecRandomDefault , 32 , data.mutableBytes);
NSString * verifier = [[[[data base64EncodedStringWithOptions: 0 ]
stringByReplacingOccurrencesOfString: @"+" withString: @"-" ]
stringByReplacingOccurrencesOfString: @"/" withString: @"_" ]
stringByTrimmingCharactersInSet:[ NSCharacterSet characterSetWithCharactersInString: @"=" ]];
Auth0 に送信して authorization_code をリクエストするため、code_verifier から code_challenge を生成します。
Javascript サンプル
// 依存関係: Node.js crypto モジュール
// https://nodejs.org/api/crypto.html#crypto_crypto
function sha256 ( buffer ) {
return crypto . createHash ( 'sha256' ). update ( buffer ). digest ();
}
var challenge = base64URLEncode ( sha256 ( verifier ));
// 依存関係: Apache Commons Codec
// https://commons.apache.org/proper/commons-codec/
// Base64クラスをインポートします。
// import org.apache.commons.codec.binary.Base64;
byte [] bytes = verifier . getBytes ( "US-ASCII" );
MessageDigest md = MessageDigest . getInstance ( "SHA-256" );
md . update (bytes, 0 , bytes . length );
byte [] digest = md . digest ();
String challenge = Base64 . encodeBase64URLSafeString (digest);
import CommonCrypto
// ...
guard let data = verifier. data ( using : . utf8 ) else { return nil }
var buffer = [ UInt8 ]( repeating : 0 , count : Int (CC_SHA256_DIGEST_LENGTH))
_ = data. withUnsafeBytes {
CC_SHA256 ( $0 . baseAddress , CC_LONG (data. count ), & buffer)
}
let hash = Data (buffer)
let challenge = hash. base64EncodedString ()
. replacingOccurrences ( of : "+" , with : "-" )
. replacingOccurrences ( of : "/" , with : "_" )
. replacingOccurrences ( of : "=" , with : "" )
Objective-C の例
// 依存関係: Apple Common Crypto ライブラリ
// http://opensource.apple.com//source/CommonCrypto
u_int8_t buffer [CC_SHA256_DIGEST_LENGTH * sizeof ( u_int8_t )];
memset (buffer, 0x 0 , CC_SHA256_DIGEST_LENGTH);
NSData * data = [verifier dataUsingEncoding: NSUTF8StringEncoding ];
CC_SHA256 ([data bytes ], (CC_LONG)[data length ], buffer);
NSData * hash = [ NSData dataWithBytes: buffer length: CC_SHA256_DIGEST_LENGTH];
NSString * challenge = [[[[hash base64EncodedStringWithOptions: 0 ]
stringByReplacingOccurrencesOfString: @"+" withString: @"-" ]
stringByReplacingOccurrencesOfString: @"/" withString: @"_" ]
stringByTrimmingCharactersInSet:[ NSCharacterSet characterSetWithCharactersInString: @"=" ]];
code_verifier と code_challenge を作成したら、ユーザーの認可を得る必要があります。技術的には、これは 認可フロー の始まりであり、このステップには次のプロセスのうち 1 つ以上が含まれる場合があります。
ユーザーを認証する。
認証を処理するため、ユーザーを IDプロバイダー にリダイレクトする。
アクティブな シングルサインオン (SSO) セッションを確認する。
以前に同意が与えられていない場合は、要求された権限レベルに対するユーザーの同意を取得する。
ユーザーを認可するには、アプリはユーザーを 認可 URL にリダイレクトする必要があります。その際、前のステップで生成した code_challenge と、code_challenge の生成に使用したメソッドを含めます。
パラメーター
カスタム API を呼び出す際にユーザーを認可する場合は、次の点に注意してください。
audience パラメーターを含める必要があります
対象 API でサポートされている追加のスコープを含めることもできます
Parameter Name Description response_typeAuth0 が返す認証情報の種類 (code または token) を示します。このフローでは、値は code でなければなりません。 code_challengecode_verifier から生成されるチャレンジです。code_challenge_methodチャレンジの生成に使用するメソッドです (例: S256) 。PKCE 仕様では S256 と plain の 2 つのメソッドが定義されています。この例では前者を使用しており、後者は非推奨のため、Auth0 でサポートされるのは S256 のみです。 client_idアプリケーションのクライアントIDです。この値は Application Settings で確認できます。 redirect_uriユーザーが認可を付与した後に、Auth0 がブラウザーをリダイレクトする先の URL です。認可コードは URL パラメーター code で受け取れます。この URL は、Application Settings で有効な callback URL として指定する必要があります。警告: OAuth 2.0 Specification に従い、Auth0 はハッシュ以降のすべてを削除し、フラグメントは無視します。 scope認可をリクエストする スコープ です。各スコープはスペースで区切る必要があります。profile や email など、ユーザーに関する 標準 OpenID Connect (OIDC) スコープ 、名前空間形式 に準拠した カスタムクレーム 、または対象 API でサポートされる任意のスコープ (例: read:contacts) をリクエストできます。リフレッシュトークン を取得するには offline_access を含めます (Application Settings で Allow Offline Access フィールドが有効になっていることを確認してください) 。 audienceモバイルアプリがアクセスする API の一意の識別子です。このチュートリアルの前提条件の一部として作成した API の Settings タブにある 識別子 の値を使用します。 state(推奨) アプリが最初のリクエストに追加する、不透明な任意の英数字文字列です。Auth0 はこれをアプリケーションへのリダイレクト時に含めます。この値を使用してクロスサイトリクエストフォージェリ (CSRF) 攻撃を防ぐ方法については、Mitigate CSRF Attacks With State Parameters を参照してください。 organization(任意) ユーザーの認証時に使用する組織の ID です。指定しない場合、アプリケーションで Display Organization Prompt が設定されていれば、ユーザーは認証時に組織名を入力できます。 invitation(任意) 組織への招待のチケット ID です。組織にメンバーを招待する 場合、ユーザーが招待を承諾したときに、アプリケーションは invitation と organization のキーと値のペアを転送して、招待の承諾を処理する必要があります。
例として、API を呼び出す際の認可 URL 用 HTML スニペットは次のようになります。
正常に処理されると、HTTP 302 レスポンスが返されます。認可コードは URL の末尾に含まれます。
HTTP / 1.1 302 Found
Location : {yourCallbackUrl}?code={authorizationCode}&state=xyzABC123
認可コードを取得したら、それをトークンと交換する必要があります。前の手順で取得した認可コード (code) を使用し、code_verifier を含めて トークンURL に POST します。
パラメーター
パラメーター名 説明 grant_type"authorization_code" に設定します。code_verifierこのチュートリアルの最初のステップで生成した、暗号学的にランダムなキーです。 codeこのチュートリアルの前のステップで取得した authorization_code です。 client_idアプリケーションのクライアントIDです。この値は Application Settings で確認できます。 redirect_uriアプリケーション設定で設定した有効なコールバック URL です。これは、このチュートリアルの前のステップで認可 URL に渡した redirect_uri と完全に一致している必要があります。なお、URL エンコードする必要があります。
正常に処理されると、access_token、refresh_token、id_token、token_type の各値を含むペイロードを含む HTTP 200 レスポンスが返されます。
{
"access_token" : "eyJz93a...k4laUWw" ,
"refresh_token" : "GEbRxBN...edjnXbL" ,
"id_token" : "eyJ0XAi...4faeEoQ" ,
"token_type" : "Bearer" ,
"expires_in" : 86400
}
IDトークン には、デコードして取り出す必要があるユーザー情報が含まれています。
アクセストークン は、Auth0 Authentication API の /userinfo エンドポイント または別の API を呼び出すために使用します。独自の API を呼び出す場合、最初に必要になるのは、アクセストークンを検証すること です。
リフレッシュトークン は、前の アクセストークン または IDトークン の有効期限が切れた後に、新しいトークンを取得するために使用します。refresh_token は、offline_access スコープを含め、Dashboard で API の Allow Offline Access を有効にした場合にのみレスポンスに含まれます。
リフレッシュトークンがあると、ユーザーは実質的に無期限で認証済みの状態を維持できるため、厳重に保管する必要があります。
ネイティブアプリケーションまたはモバイルアプリケーションから API を呼び出すには、アプリケーションで取得したアクセストークンを Bearer トークンとして HTTP リクエストの Authorization ヘッダーに渡す必要があります。
このチュートリアルに沿って進め、次の内容を完了していれば、すでにリフレッシュトークン を受け取っています。
新しいアクセストークンを取得するには、リフレッシュトークン を使用できます。通常、ユーザーが新しいアクセストークンを必要とするのは、前のトークンの有効期限が切れた後か、新しいリソースへのアクセスが初めて必要になったときだけです。API を呼び出すたびに新しいアクセストークンを取得するためにこのエンドポイントを呼び出すのはベストプラクティスではありません。また、Auth0 ではレート制限が適用されているため、同じ IP から同じトークンを使用してそのエンドポイントに実行できるリクエスト数は制限されます。
トークンを更新するには、grant_type=refresh_token を使用して、Authentication API の /oauth/token エンドポイントに POST リクエストを送信します。
トークンURLへのPOSTの例
パラメーター
パラメーター名 説明 grant_typerefresh_token に設定します。client_idアプリケーションのクライアントIDです。この値は Application Settings で確認できます。 refresh_token使用するリフレッシュトークンです。 scope(任意) 要求するスコープをスペース区切りで指定します。指定しない場合は元のスコープが使用されます。指定した場合は、元のスコープより少ない範囲を要求できます。なお、URL エンコードが必要です。
正常に処理されると、新しい access_token、その有効期間 (秒単位、expires_in) 、付与された scope 値、token_type を含むペイロードとともに HTTP 200 レスポンスが返されます。初期トークンのスコープに openid が含まれている場合、レスポンスには新しい id_token も含まれます。
{
"access_token" : "eyJ...MoQ" ,
"expires_in" : 86400 ,
"scope" : "openid offline_access" ,
"id_token" : "eyJ...0NE" ,
"token_type" : "Bearer"
}
Actions を使用すると、返されるアクセストークンのスコープを変更したり、アクセストークンやIDトークンにクレームを追加したりできます。 (Actions の詳細については、Auth0 Actions を参照してください。) これを行うには、ユーザーの認証後に実行される次の Action を追加します。
exports . onExecutePostLogin = async ( event , api ) => {
// アクセストークンとIDトークンにカスタムクレームを追加する
api . accessToken . setCustomClaim ( 'https://foo/bar' , 'value' );
api . idToken . setCustomClaim ( 'https://fiz/baz' , 'some other value' );
// アクセストークンのスコープを変更する
api . accessToken . addScope ( 'foo' );
api . accessToken . addScope ( 'bar' );
};
スコープは、Action の実行後にトークン内で利用できるようになります。
サンプルアプリケーションを見る: モバイルアプリ + API
実装例については、アーキテクチャシナリオのモバイル + API を参照してください。この一連のチュートリアルには、GitHubで参照できるコードサンプル も用意されています。