ASP.NETIdentityでIdentityServer 4を使用して認証機能を追加した後、ユーザーが自分のgoogle +アカウントでログインできるようにGoogleプロバイダーを追加する予定です。フロントエンドとしてAngularを使用し、バックエンドとしてASP.NET Web Api(コア)を使用しています。
// Login client
public login(email: string, password: string): Observable<any> {
let body: any = this.encodeParams({ /* cliend_id, grant_type, username, password, scope */ });
return this.http.post("http://localhost:64023/connect/token", body, this.options)
.map((res: Response) => {
const body: any = res.json();
if (typeof body.access_token !== "undefined") {
// Set localStorage with id_token,..
}
}).catch((error: any) => { /**/ );
}
// Register Web API
[HttpPost("Create")]
[AllowAnonymous]
public async Task<IActionResult> Create([FromBody]CreateUserViewModel model)
{
var user = new ApplicationUser
{
FirstName = model.FirstName,
LastName = model.LastName,
AccessFailedCount = 0,
Email = model.Email,
EmailConfirmed = false,
LockoutEnabled = true,
NormalizedEmail = model.Email.ToUpper(),
NormalizedUserName = model.Email.ToUpper(),
TwoFactorEnabled = false,
UserName = model.Email
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await addToRole(model.Email, "user");
await addClaims(model.Email);
}
return new JsonResult(result);
}
// Identity Server Startup
app.UseGoogleAuthentication(new GoogleOptions
{
AuthenticationScheme = "Google",
DisplayName = "Google",
SignInScheme = "Identity.External",
// ClientId, ClientSecret,..
});
ユーザーがログインすると、localStorageが設定され、安全なコントローラーを保護できるようになります。 Googleプロバイダーのために、追加のボタンと次の方法を追加しました。
initGoogleAPI() {
let self = this;
gapi.load('auth2', function () {
self.auth2 = gapi.auth2.init({ /* client_id, cookiepolicy, scope */ });
self.externalLogin(document.getElementById('google-button'));
});
}
externalLogin(element) {
let self = this;
this.auth2.attachClickHandler(element, {},
function (googleUser) {
// retrieved the id_token, name, email,...
}, function (error) {
alert(JSON.stringify(error, undefined, 2));
});
}
私はいくつかの解決策を見つけましたが、MVCアプリケーションのみであり、クライアントサイドフレームワークを使用するSPAではありません。外部ログインを機能させるには、次にどのような手順を実行する必要がありますか?ユーザーが外部プロバイダーを使用して初めてサインインするときに、AspNetUsersテーブルに新しいレコードを作成する必要がありますか?
このリポジトリを確認できます。ids4サーバープロジェクトを無視して、angularクライアントを確認してください。openidクライアントを使用してこれを行う必要があります。そうすると、クライアントはids4プロジェクトのログインページにリダイレクトされます。ログインすると、後で使用できるように保存したトークンが返されます
https://github.com/nertilpoci/Aspnetcore-identityserver4-webapi-angular
https://github.com/nertilpoci/Aspnetcore-identityserver4-webapi-angular/tree/master/ClientApp
ログインはidentityServer4によって処理される必要があります。ローカルログインやサードパーティに関係なく、同様のトークンをWebアプリに返す必要があります。
Web APIバックエンドでユーザーデータが必要な場合は、クレームとして渡す必要があります。
IdentityServer4クイックスタートはコードに役立つ場合があります。IdentityServerに追加する 外部ログイン の例と、 JavaScriptアプリケーション からログインフローを実行する方法があります。
完璧なユーザーエクスペリエンスの観点からこれに苦労することが多かったので、ここにいくつかの考えを投稿したいと思いました。私はずっと前に手動のアプローチを使用してこれを実装することができました。これがIdentityServerのものであるかどうかはわかりませんが、それでも、IdentityServerと連携してASP.NETIdentityシステムの一部として実装できます。
注:理想的なシナリオは、ユーザーをIDプロバイダーのログインページ(この場合はIdentity Server)に誘導することにより、ユーザーにGoogle/FBへのログイン/登録を許可することです。そこでGoogleまたはFBにログインし、IdentityServerによってアプリケーションにリダイレクトされます。これは、暗黙的またはPKCEフローです。 PKCEには追加の手順がありますが、クレーム処理などはすべてIdSrvコードによって行われます。これは、最も広く文書化されているものです。
最も頻繁に要求されるシナリオ
私は、多くの場合、クライアントは、我々は単にそれに対応するJavaScriptライブラリを経由してJavascriptをGoogleやFBログインを使用することを要求しなければなりませんでした。これはあなたが話しているシナリオです。この場合のフローは次のようになります。
ユーザーがあなたのangularまたはreactアプリページに到達します
Google/FBを使用してサインインします。これにより、「外部アクセストークン」が提供されます。つまり、GoogleとFBのトークン
次に、このトークン情報を取得して、追加情報(クライアントID、テナントIDなど)とともに、作成できる新しいAPIメソッドに渡すことができます。
APIは、内部的に信憑性のために外部アクセストークンを検証する「ローカル・トークンPOSTのAPI /」と呼ばれる方法を持つことができます。アウト情報をチェックする場合は、ユーザーのアクセストークンベアラー新しいアプリケーションの特定を生成し、Javascriptのアプリへの応答バックとして渡します。 JSアプリは、外部アクセストークン(FB/Google APIリクエスト用)とアプリケーションのアクセストークン(アプリケーションAPIリクエスト用)の両方をCookie /ローカルストレージに保存し、そこから続行できます。
私はこれを非常に簡単に行うことができました。
アクセストークンを生成したい場合、それはかなりstr8フォワードです。これが私のコードの例です。これにより、IdentityServerが生成するのと同様の応答が文字通り生成されます。
public static async Task<JObject> GenerateLocalAccessTokenResponse(string userName, string role, string userId, string clientId, string provider)
{
var tokenExpiration = TimeSpan.FromDays(1);
var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim("ClientId", clientId));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId));
identity.AddClaim(new Claim(ClaimTypes.Role, role));
var data = new Dictionary<string, string>
{
{"userName", userName},
{"client_id", clientId},
{"role", role},
{"provider", provider},
{"userId", userId}
};
var props = new AuthenticationProperties(data);
var ticket = new AuthenticationTicket(identity, props);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
var tokenResponse = new JObject(
new JProperty("userName", userName),
new JProperty("client_id", clientId),
new JProperty("role", role),
new JProperty("provider", provider),
new JProperty("userId", userId),
new JProperty("access_token", accessToken),
new JProperty("token_type", "bearer"),
new JProperty("expires_in", tokenExpiration.TotalSeconds.ToString()),
new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
);
return tokenResponse;
}