//[Authorize] public Dictionary <string, string> OAuth2BearerToken2(FormDataCollection formData) { // 戻り値 // ・正常 Dictionary <string, string> ret = new Dictionary <string, string>(); // ・異常 Dictionary <string, string> err = new Dictionary <string, string>(); // 変数 string grant_type = formData[OAuth2AndOIDCConst.grant_type]; string assertion = formData[OAuth2AndOIDCConst.assertion]; // クライアント認証 if (grant_type == OAuth2AndOIDCConst.JwtBearerTokenFlowGrantType) { Dictionary <string, string> dic = JsonConvert.DeserializeObject <Dictionary <string, string> >( CustomEncode.ByteToString(CustomEncode.FromBase64UrlString( assertion.Split('.')[1]), CustomEncode.us_ascii)); string pubKey = OAuth2Helper.GetInstance().GetJwtAssertionPublickey(dic[OAuth2AndOIDCConst.iss]); pubKey = CustomEncode.ByteToString(CustomEncode.FromBase64String(pubKey), CustomEncode.us_ascii); if (!string.IsNullOrEmpty(pubKey)) { string iss = ""; string aud = ""; string scopes = ""; JObject jobj = null; if (JwtAssertion.VerifyJwtBearerTokenFlowAssertion(assertion, out iss, out aud, out scopes, out jobj, pubKey)) { // aud 検証 if (aud == ASPNETIdentityConfig.OAuth2AuthorizationServerEndpointsRootURI + ASPNETIdentityConfig.OAuth2BearerTokenEndpoint2) { // ここからは、JwtAssertionではなく、JwtTokenを作るので、属性設定に注意。 ClaimsIdentity identity = OAuth2Helper.AddClaim( new ClaimsIdentity(OAuthDefaults.AuthenticationType), iss, "", scopes.Split(' '), ""); AuthenticationProperties prop = new AuthenticationProperties(); prop.IssuedUtc = DateTimeOffset.UtcNow; prop.ExpiresUtc = DateTimeOffset.Now.Add(ASPNETIdentityConfig.OAuth2AccessTokenExpireTimeSpanFromMinutes); // token_type ret.Add(OAuth2AndOIDCConst.token_type, OAuth2AndOIDCConst.Bearer.ToLower()); // access_token AccessTokenFormatJwt verifier = new AccessTokenFormatJwt(); string access_token = verifier.Protect(new AuthenticationTicket(identity, prop)); ret.Add(OAuth2AndOIDCConst.AccessToken, access_token); // expires_in jobj = (JObject)JsonConvert.DeserializeObject( CustomEncode.ByteToString(CustomEncode.FromBase64UrlString( access_token.Split('.')[1]), CustomEncode.us_ascii)); ret.Add("expires_in", (long.Parse((string)jobj[OAuth2AndOIDCConst.exp]) - long.Parse((string)jobj[OAuth2AndOIDCConst.iat])).ToString()); // オペレーション・トレース・ログ出力 string clientName = OAuth2Helper.GetInstance().GetClientName(iss); Logging.MyOperationTrace(string.Format( "{0}({1}) passed the 'jwt bearer token flow' by {2}({3}).", iss, clientName, iss, clientName)); return(ret); // 成功 } else { // クライアント認証エラー(Credential(aud)不正 err.Add("error", "invalid_client"); err.Add("error_description", "Invalid credential"); } } else { // クライアント認証エラー(Credential(署名)不正 err.Add("error", "invalid_client"); err.Add("error_description", "Invalid credential"); } } else { // クライアント認証エラー(Credential(iss or pubKey)不正 err.Add("error", "invalid_client"); err.Add("error_description", "Invalid credential"); } } else { // grant_type パラメタ・エラー err.Add("error", "invalid_request"); err.Add("error_description", "invalid grant_type"); } return(err); // 失敗 }
/// <summary> /// Resource Owner Password Credentials Grantのカスタム認証ロジック /// TokenEndpointPathへの grant_type = password アクセスは、こちらに到達する。 /// ・context.Username および context.Password を検証する。 /// ・クライアントは"access_token" および "refresh_token" を取得する。 /// </summary> /// <param name="context">OAuthGrantResourceOwnerCredentialsContext</param> /// <returns>Task</returns> /// <see cref="https://msdn.microsoft.com/ja-jp/library/dn343587.aspx"/> public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { if (!ASPNETIdentityConfig.EnableResourceOwnerPasswordCredentialsGrantType) { throw new NotSupportedException(Resources.ApplicationOAuthBearerTokenProvider.EnableResourceOwnerCredentialsGrantType); } // この実装は、ValidateClientAuthenticationの続きで、ClientのOAuth権限を確認する。 // 権限がある場合、Resource Owner Password Credentialsグラント種別の処理フローを継続する。 try { // ApplicationUser を取得する。 ApplicationUserManager userManager = HttpContext.Current.GetOwinContext().GetUserManager <ApplicationUserManager>(); // username=ユーザ名&password=パスワードとして送付されたクレデンシャルを検証する。 ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); if (user != null) { // ユーザーが見つかった場合。 try { // ユーザーに対応するClaimsIdentityを生成する。 ClaimsIdentity identity = await userManager.CreateIdentityAsync(user, OAuthDefaults.AuthenticationType); // ClaimsIdentityに、その他、所定のClaimを追加する。 OAuth2Helper.AddClaim(identity, context.ClientId, "", context.Scope, ""); // 検証完了 context.Validated(identity); // オペレーション・トレース・ログ出力 Logging.MyOperationTrace(string.Format("{0}({1}) passed the 'resource owner password credentials flow' by {2}({3}).", user.Id, user.UserName, context.ClientId, OAuth2Helper.GetInstance().GetClientName(context.ClientId))); } catch { // ClaimManagerIdentityは、UserManagerで作成できませんでした。 context.SetError( "server_error", Resources.ApplicationOAuthBearerTokenProvider.server_error2); // 拒否 context.Rejected(); } } else { // ユーザーが見つからなかった場合。 // Resources Ownerの資格情報が無効であるか、Resources Ownerが存在しません。 context.SetError( "access_denied", Resources.ApplicationOAuthBearerTokenProvider.access_denied); // 拒否 context.Rejected(); } } catch { // ユーザーを取得できませんでした。 context.SetError( "server_error", Resources.ApplicationOAuthBearerTokenProvider.server_error1); // 拒否 context.Rejected(); } }
/// <summary> /// Client Credentialsグラント種別のカスタム認証ロジック /// TokenEndpointPathへの grant_type=client_credentials アクセスは、こちらに到達する。 /// ・client_id, client_secret の検証は、(2) ValidateClientAuthenticationで済。 /// ・クライアントは"access_token"を取得する。 /// </summary> /// <param name="context">OAuthGrantClientCredentialsContext</param> /// <returns>Task</returns> /// <see cref="https://msdn.microsoft.com/ja-jp/library/dn343586.aspx"/> public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context) { if (!ASPNETIdentityConfig.EnableClientCredentialsGrantType) { throw new NotSupportedException(Resources.ApplicationOAuthBearerTokenProvider.EnableClientCredentialsGrantType); } // ASP.Net MVC: Creating an OAuth client credentials grant type token endpoint // http://www.hackered.co.uk/articles/asp-net-mvc-creating-an-oauth-client-credentials-grant-type-token-endpoint // WEB API 2 OAuth Client Credentials Authentication, How to add additional parameters? - Stack Overflow // http://stackoverflow.com/questions/29132031/web-api-2-oauth-client-credentials-authentication-how-to-add-additional-paramet try { ApplicationUser user = null; ApplicationUserManager userManager = HttpContext.Current.GetOwinContext().GetUserManager <ApplicationUserManager>(); // client_idに対応するApplicationUserを取得する。 user = await userManager.FindByNameAsync( OAuth2Helper.GetInstance().GetClientName(context.ClientId)); // ClaimsIdentity ClaimsIdentity identity = null; if (user != null) { // User Accountの場合、 // ユーザーに対応するClaimsIdentityを生成する。 identity = await userManager.CreateIdentityAsync(user, OAuthDefaults.AuthenticationType); // ClaimsIdentityに、その他、所定のClaimを追加する。 identity = OAuth2Helper.AddClaim(identity, context.ClientId, "", context.Scope, ""); // オペレーション・トレース・ログ出力 Logging.MyOperationTrace( string.Format("{0}({1}) passed the 'client credentials flow' by {2}({3}).", user.Id, user.UserName, context.ClientId, OAuth2Helper.GetInstance().GetClientName(context.ClientId))); // 検証完了 context.Validated(identity); } else { // Client Accountの場合、 string clientName = OAuth2Helper.GetInstance().GetClientName(context.ClientId); if (string.IsNullOrEmpty(clientName)) { // 検証失敗 context.Rejected(); } else { // ClaimsIdentityを自前で生成する。 identity = new ClaimsIdentity(context.Options.AuthenticationType); // Name Claimを追加 identity.AddClaim(new Claim(ClaimTypes.Name, OAuth2Helper.GetInstance().GetClientName(context.ClientId))); // ClaimsIdentityに、その他、所定のClaimを追加する。 identity = OAuth2Helper.AddClaim(identity, context.ClientId, "", context.Scope, ""); // オペレーション・トレース・ログ出力 Logging.MyOperationTrace(string.Format( "Passed the 'client credentials flow' by {0}({1}).", context.ClientId, clientName)); // 検証完了 context.Validated(identity); } } } catch { // ユーザーを取得できませんでした。 context.SetError( "server_error", Resources.ApplicationOAuthBearerTokenProvider.server_error1); // 拒否 context.Rejected(); } }
/// <summary>Unprotect</summary> /// <param name="jwt">JWT文字列</param> /// <returns>AuthenticationTicket</returns> public AuthenticationTicket Unprotect(string jwt) { // 空のケースあり。 if (string.IsNullOrEmpty(jwt)) { return(null); } // 検証 JWT_RS256 jwtRS256 = new JWT_RS256(ASPNETIdentityConfig.OAuthJWT_cer, ASPNETIdentityConfig.OAuthJWTPassword); if (jwtRS256.Verify(jwt)) { // 検証できた。 // デシリアライズ、 string[] temp = jwt.Split('.'); string json = CustomEncode.ByteToString(CustomEncode.FromBase64UrlString(temp[1]), CustomEncode.UTF_8); Dictionary <string, object> authTokenClaimSet = JsonConvert.DeserializeObject <Dictionary <string, object> >(json); // 以下の検証処理 // ★ "iss": accounts.google.com的な, // ★ "aud": client_id(クライアント識別子) // ★ "sub": ユーザーの一意識別子(uname, email) // ★ "exp": JWT の有効期限(Unix時間) // ☆ "jti": JWT のID(OAuth Token Revocation) DateTime?datetime = OAuth2RevocationProvider.GetInstance().Get((string)authTokenClaimSet["jti"]); if (datetime == null) { // authToken.iss, authToken.expの検証 if ((string)authTokenClaimSet["iss"] == ASPNETIdentityConfig.OAuthIssuerId && OAuth2Helper.GetInstance().GetClientSecret((string)authTokenClaimSet["aud"]) != null && long.Parse((string)authTokenClaimSet["exp"]) >= DateTimeOffset.Now.ToUnixTimeSeconds()) { // authToken.subの検証 // ApplicationUser を取得する。 ApplicationUserManager userManager = HttpContext.Current.GetOwinContext().GetUserManager <ApplicationUserManager>(); ApplicationUser user = userManager.FindByName((string)authTokenClaimSet["sub"]); // 同期版でOK。 if (user != null) { // User Accountの場合 // ユーザーに対応するClaimsIdentityを生成し、 ClaimsIdentity identity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ExternalBearer); // aud、scopes、nonceなどのClaimを追加する。 List <string> scopes = new List <string>(); foreach (string s in (JArray)authTokenClaimSet["scopes"]) { scopes.Add(s); } OAuth2Helper.AddClaim(identity, (string)authTokenClaimSet["aud"], "", scopes, (string) authTokenClaimSet["nonce"]); // その他、所定のClaimを追加する。 identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_ExpirationTime, (string)authTokenClaimSet["exp"])); identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_NotBefore, (string)authTokenClaimSet["nbf"])); identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_IssuedAt, (string)authTokenClaimSet["iat"])); identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_JwtId, (string)authTokenClaimSet["jti"])); // AuthenticationPropertiesの生成 AuthenticationProperties prop = new AuthenticationProperties(); prop.IssuedUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet["iat"])); prop.ExpiresUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet["exp"])); AuthenticationTicket auth = new AuthenticationTicket(identity, prop); // 認証結果を返す。 return(auth); } else { // Client Accountの場合 // ClaimとStoreのAudience(aud)に対応するSubject(sub)が一致するかを確認し、一致する場合のみ、認証する。 // ※ でないと、UserStoreから削除されたUser Accountが、Client Accountに化けることになる。 if ((string)authTokenClaimSet["sub"] == OAuth2Helper.GetInstance().GetClientName((string)authTokenClaimSet["aud"])) { // ClaimsIdentityを生成し、 ClaimsIdentity identity = new ClaimsIdentity(DefaultAuthenticationTypes.ExternalBearer); // sub(client_idに対応するclient_name)Claimを設定する。 identity.AddClaim(new Claim(ClaimTypes.Name, (string)authTokenClaimSet["sub"])); // aud、scopes、nonceなどのClaimを追加する。 List <string> scopes = new List <string>(); foreach (string s in (JArray)authTokenClaimSet["scopes"]) { scopes.Add(s); } OAuth2Helper.AddClaim(identity, (string)authTokenClaimSet["aud"], "", scopes, (string)authTokenClaimSet["nonce"]); // その他、所定のClaimを追加する。 identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_ExpirationTime, (string)authTokenClaimSet["exp"])); identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_NotBefore, (string)authTokenClaimSet["nbf"])); identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_IssuedAt, (string)authTokenClaimSet["iat"])); identity.AddClaim(new Claim(ASPNETIdentityConst.Claim_JwtId, (string)authTokenClaimSet["jti"])); // AuthenticationPropertiesの生成 AuthenticationProperties prop = new AuthenticationProperties(); prop.IssuedUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet["iat"])); prop.ExpiresUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet["exp"])); AuthenticationTicket auth = new AuthenticationTicket(identity, prop); // 認証結果を返す。 return(auth); } } } else { // クレーム検証の失敗 } } else { // 取り消し済み } } else { // JWT署名検証の失敗 } // 検証、認証ナドナド、できなかった。 return(null); }