ここでは、React や Angular などの JS フレームワークで開発したアプリケーションの認証フローを実装する方法を説明します。GeneXus 以外で開発されたアプリケーションで、OAuth2.0 プロトコルを介して GAM で認証する必要がある場合の説明も含まれます。
このソリューションのアーキテクチャは、次の 3 つの参加レイヤーに分かれています:
GAM と IDP 間の通信は通常と変わりないので、ここでは取り上げません。
次にフローの全体図を示します。手順に沿って説明します。
エントリーを有効にするために認証を必要とするパネルにユーザーがアクセスする場合の、あらゆる認証の基本となります。ここでセッションがチェックされ、有効であった場合は、認証フローを続ける必要はありません。有効でなかった場合は手順 2 に進みます。
認証なしでアクセスが試みられたことをフロントエンドが検出すると、ユーザー認証を許可するためにパネルへのリダイレクトが求められます。最初にアクセスしたパネルを離れる前に、ログインフローを終了するため、そのパネルの URL をクライアントストレージに保存します。
ユーザーが認証タイプを選択します。外部認証の場合は、GAM の外部ログインフローがトリガーされます。
この処理は、次のデータを、GAM /oauth/access_token の API に POST することで行われます。
本文
- client_id - このアプリケーションを特定するために GAM で設定する必要があるパラメーターです。
- client_secret - このアプリケーションを特定するために GAM で設定する必要があるパラメーターです。
- grant_type - GAM で事前に設定されている認証タイプを示します (Facebook など)。
- username - ユーザーの名前で、認証タイプ GAMRemoteRest でのみ使用されます。
- password - ユーザーのパスワードです。username と同じく、GAMRemoteRest でのみ使用されます。
- scope - この値は FullControl に設定する必要があります。
- additional_parameters - GAM が受け入れる追加パラメーターです。たとえば、同じタイプの認証が複数ある場合は、この追加パラメーターで名前を指定する必要があります。JSON 形式で使用されます。
- avoid_redirect - この値は常に True に設定する必要があります。
ヘッダー
- 'Accept': 'application/json'
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Genexus-Agent': 'SmartDevice Application',
- 'avoid_redirect': "true",
- 'redirect_clientloginurl' - このヘッダーは、IDP の返答の後に認証のコールバックがリダイレクトされるクライアントの URL を GAM に知らせるために必要です (手順 7 を参照)。
次に、この実装と関連付けられたコードが、GeneXus で提供される Angular ジェネレーターに配置されます。
public loginExternal(type: string, username: string, password: string, additionalParameters: any = null) {
const url = Settings.OAUTH_ENDPOINT + 'access_token';
let body = "client_id=" + Settings.GAM_CLIENT_ID;
body += "&client_secret=" + Settings.GAM_CLIENT_SECRET;
body += "&grant_type=" + type;
body += "&username=" + username;
body += "&password=" + password
body += "&scope=FullControl";
if (additionalParameters) {
body += "&additional_parameters=" + JSON.stringify(additionalParameters);
}
body += "&avoid_redirect=true"
var angularCallbackUrl = this.getLoginInitiator(false);
const headers = new HttpHeaders(
{
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Genexus-Agent': 'SmartDevice Application',
'avoid_redirect': "true",
'redirect_clientloginurl': angularCallbackUrl
});
return this._http.post(url, body,{ headers: headers, withCredentials: true } ).pipe(
map((response: any) => {
/*このコードは手順 5 を参照*/
}), catchError(err => {
return throwError(err);
})
);
}
GAM への POST が完了すると、GAM は、選択された認証タイプの設定と、外部ログイン用に GAM に送られたデータに基づいて URL を構成します。この URL がリダイレクトされ、認証を行うように設定されている IDP のサイトに移動します。GAM は HTTP 応答とステータス 301 で応答し、リダイレクト先のエンドポイントに対応する場所を示します。
応答で示された場所で取得した URL を使用して、次のアクションが実行されます:
- GAM が送信する STATE パラメーターを取得するための検索が実行されます (現在のフローの ID)。認証の最後に追加のセキュリティチェックを実行するために、その状態がクライアントストレージに保存されます。
- GAM が送信した URL へのリダイレクトが実行されます。
この時点で実行フローの制御が失われます。これは、IDP へのリダイレクトが実行されたためです。
この実装と関連付けられたコードが、GeneXus で提供される Angular ジェネレーターに配置されます。
ClientStorage.Set('gx.GAM.on_external_login', "true");
let url = response.Location;
var regEx = RegExp("state=(^&*)")
var state = url.match(regEx)
if (state != null)
{
ClientStorage.Set('gx.GAM.login.state', state1);
window.location.href = url //リダイレクトを実行
}
else
return throwError("The state is empty");
この手順は、GAM と IDP 間のフローに対応します。GAM と IDP 間の対応するやり取りが終了すると、IDP は、認証タイプの事前設定で確立した GAM エンドポイントのリダイレクトを実行して、フローを終了します。
この時点で GAM はセッションを作成しており、外部認証フローが正常に完了したことをフロントエンドに伝えます。
この通信は、手順 3 で、認証の最初に GAM に送られたコールバック URL へのリダイレクトによって行われます。
このリダイレクトには次のパラメーターが含まれています。
- access_token - 成功した認証によって生成されたトークンです。
- state - フローを識別する state (状態) パラメーターです。
- refresh_token - 有効期限が切れた後にアクセストークンを更新するために使用されるトークンです。
access_token および state パラメーターは必須パラメーターであり、GAM からのリクエストの場合は必ず指定されます。
重要: ここで説明したパラメーターが空ではないこと、および受け取った state パラメーターが手順 4 で収集されたものと一致することを確認する必要があります。それらの確認がとれれば、認証は成功したことになり、対応するセッションの記録がクライアントで行われます。
この実装と関連付けられたコードが、GeneXus で提供される Angular ジェネレーターに配置されます。
var access_token = this.getQueryParameter("access_token")
var state = this.getQueryParameter("state")
var refresh_token = this.getQueryParameter("refresh_token")
if (access_token && state)
{
if (state == ClientStorage.Get("gx.GAM.login.state"))
{
ClientStorage.Remove('gx.GAM.login.state');
this._authService.finishLogin(access_token, refresh_token);
return true;
}
}
手順 7 で取得したアクセストークンを使用して、GAM にユーザー情報を取得するためのリクエストを行います。これは、
Userinfo GAM サービスの説明に従って実行できます。
この時点で成功したログインを記録する処理がすべて終わり、access_token およびすべてのユーザー情報が、クライアントストアまたはフレームワークで提供される安全な場所に保存されます。
最後の手順は、ログインイニシエーターのパネル、つまりユーザーが最初にアクセスを試みていたパネルへの最終的なリダイレクトです。認証が行われたため、手順 1 のフローが有効なトークンで実行され、フローがすべて完了します。