Ejemplo n.º 1
0
        /// <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
            //var client = clientService.GetClient(context.ClientId);

            // 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(
                    OAuth2ProviderHelper.GetInstance().GetClientName(context.ClientId));

                if (user == null)
                {
                    // *.configに定義したclient_idの場合は、アカウントが存在しない。
                    // その場合、どうするか?は案件毎に検討する(既定では、既定の管理者ユーザを使用する)。
                    user = await userManager.FindByNameAsync(ASPNETIdentityConfig.AdministratorUID);

                    // ClaimsIdentityを自前で生成する場合、
                    //ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
                    //・・・
                }

                // ユーザーに対応するClaimsIdentityを生成する。
                ClaimsIdentity identity = await userManager.CreateIdentityAsync(
                    user, DefaultAuthenticationTypes.ExternalBearer);

                // ClaimsIdentityに、その他、所定のClaimを追加する。
                OAuth2ProviderHelper.AddClaim(identity, context.ClientId, "", "", context.Scope);

                // 検証完了
                context.Validated(identity);

                // オペレーション・トレース・ログ出力
                Logging.MyOperationTrace(string.Format("{0}({1}) passed the 'client credentials flow' by {2}({3}).",
                                                       user.Id, user.UserName, context.ClientId, OAuth2ProviderHelper.GetInstance().GetClientName(context.ClientId)));
            }
            catch
            {
                // ユーザーを取得できませんでした。
                context.SetError(
                    "server_error",
                    Resources.ApplicationOAuthBearerTokenProvider.server_error1);

                // 拒否
                context.Rejected();
            }
        }
Ejemplo n.º 2
0
        /// <summary>Unprotect</summary>
        /// <param name="jwt">JWT文字列</param>
        /// <returns>AuthenticationTicket</returns>
        public AuthenticationTicket Unprotect(string jwt)
        {
            // 検証
            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":"クライアント識別子.apps.googleusercontent.com",
                //  ★ "sub":"ユーザーの一意識別子",
                //  ★ "exp":JWT の有効期限(Unix時間)
                //  ☆ "nonce":Implicitで必須

                // authToken.iss, authToken.expの検証
                if ((string)authTokenClaimSet["iss"] == ASPNETIdentityConfig.OAuthIssuerId &&
                    OAuth2ProviderHelper.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);

                        // ClaimsIdentityに、その他、所定のClaimを追加する。
                        List <string> scopes = new List <string>();
                        foreach (string s in (JArray)authTokenClaimSet["scopes"])
                        {
                            scopes.Add(s);
                        }
                        OAuth2ProviderHelper.AddClaim(identity,
                                                      (string)authTokenClaimSet["aud"],
                                                      "", (string)authTokenClaimSet["nonce"], scopes);

                        // AuthenticationPropertiesの生成
                        AuthenticationProperties prop = new AuthenticationProperties();

                        // AuthenticationTicketに格納不要
                        //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に対応するSubjectが一致するかを確認し、一致する場合のみ、認証する。
                        // でないと、UserStoreから削除されたUser Accountが、Client Accountに化けることになる。
                        if ((string)authTokenClaimSet["sub"] == OAuth2ProviderHelper.GetInstance().GetClientName((string)authTokenClaimSet["aud"]))
                        {
                            // ClaimsIdentityを生成し、
                            ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);

                            // ClaimsIdentityに、client_idに対応するclient_nameを設定する。
                            identity.AddClaim(new Claim(ClaimTypes.Name, (string)authTokenClaimSet["sub"]));

                            // ClaimsIdentityに、その他、所定のClaimを追加する。
                            List <string> scopes = new List <string>();
                            foreach (string s in (JArray)authTokenClaimSet["scopes"])
                            {
                                scopes.Add(s);
                            }

                            OAuth2ProviderHelper.AddClaim(identity,
                                                          (string)authTokenClaimSet["aud"],
                                                          "", (string)authTokenClaimSet["nonce"], scopes);

                            // AuthenticationPropertiesの生成
                            AuthenticationProperties prop = new AuthenticationProperties();

                            // AuthenticationTicketに格納不要
                            //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);
                        }
                    }
                }
            }

            // 検証、認証ナドナド、できなかった。
            return(null);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Authorization Code、Implicitグラント種別において、
        /// AuthorizeEndpointPathを処理する場合に発生する。
        /// 以下の両方の要素を検証する処理を実装するためのメソッド。
        /// ・context.ClientId が、登録された "client_id" であること。
        /// ・context.RedirectUri が、そのクライアント用に登録された "redirect_uri" であること。
        /// </summary>
        /// <param name="context">OAuthValidateClientRedirectUriContext</param>
        /// <returns>Task</returns>
        /// <see cref="https://msdn.microsoft.com/ja-jp/library/dn385496.aspx"/>
        public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            // context.Validatedに事前登録したRedirectエンドポイントを指定して呼び出し、contextを検証完了に設定する。
            // ・ 検証完了にしなければ要求はそれ以上先には進まない。
            // ・ RFC上の記載で、RedirectEndpointのURIは、AbsoluteUriである必要があるとの記載あり。
            //    ASP.NET IdentityのチェックでAbsoluteUriである必要があるとの記載あり形式でないと弾かれる。

            // response_type
            string response_type = context.Request.Query.Get("response_type");

            if (!string.IsNullOrEmpty(response_type))
            {
                if (response_type.ToLower() == "code")
                {
                    if (!ASPNETIdentityConfig.EnableAuthorizationCodeGrantType)
                    {
                        throw new NotSupportedException(Resources.ApplicationOAuthBearerTokenProvider.EnableAuthorizationCodeGrantType);
                    }
                }
                else if (response_type.ToLower() == "token")
                {
                    if (!ASPNETIdentityConfig.EnableImplicitGrantType)
                    {
                        throw new NotSupportedException(Resources.ApplicationOAuthBearerTokenProvider.EnableImplicitGrantType);
                    }
                }
            }

            // redirect_uri
            string redirect_uri = context.RedirectUri;

            #region redirect_uriのチェック
            if (string.IsNullOrEmpty(redirect_uri))
            {
                // redirect_uriの指定が無い。

                // クライアント識別子に対応する事前登録したredirect_uriを取得する。
                redirect_uri = OAuth2ProviderHelper.GetInstance().GetClientsRedirectUri(context.ClientId, response_type);

                if (!string.IsNullOrEmpty(redirect_uri))
                {
                    // 事前登録されている。
                    if (redirect_uri.ToLower() == "test_self_code")
                    {
                        // Authorization Codeグラント種別のテスト用のセルフRedirectエンドポイント
                        context.Validated(
                            ASPNETIdentityConfig.OAuthClientEndpointsRootURI
                            + ASPNETIdentityConfig.OAuthAuthorizationCodeGrantClient_Account);
                    }
                    else if (redirect_uri.ToLower() == "test_self_token")
                    {
                        // Implicitグラント種別のテスト用のセルフRedirectエンドポイント
                        context.Validated(
                            ASPNETIdentityConfig.OAuthClientEndpointsRootURI
                            + ASPNETIdentityConfig.OAuthImplicitGrantClient_Account);
                    }
                    else
                    {
                        // 事前登録した、redirect_uriをそのまま使用する。
                        context.Validated(redirect_uri);
                    }
                }
                else
                {
                    // 事前登録されていない。
                }
            }
            else
            {
                // redirect_uriの指定が有る。

                // 指定されたredirect_uriを使用する場合は、チェックが必要になる。
                if (
                    // self_code : Authorization Codeグラント種別
                    redirect_uri == (ASPNETIdentityConfig.OAuthClientEndpointsRootURI + ASPNETIdentityConfig.OAuthAuthorizationCodeGrantClient_Manage)
                    )
                {
                    // 不特定多数のクライアント識別子に許可されたredirect_uri
                    context.Validated(redirect_uri);
                }
                else
                {
                    // クライアント識別子に対応する事前登録したredirect_uriに
                    string preRegisteredUri = OAuth2ProviderHelper.GetInstance().GetClientsRedirectUri(context.ClientId, response_type);

                    //if (redirect_uri.StartsWith(preRegisteredUri))
                    if (redirect_uri == preRegisteredUri)
                    {
                        // 完全一致する場合。
                        context.Validated(redirect_uri);
                    }
                    else
                    {
                        // 完全一致しない場合。
                        context.SetError(
                            "server_error",
                            Resources.ApplicationOAuthBearerTokenProvider.Invalid_redirect_uri);
                    }
                }
            }
            #endregion

            // 結果を返す。
            return(Task.FromResult(0));
        }
Ejemplo n.º 4
0
        /// <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.EnableResourceOwnerCredentialsGrantType)
            {
                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, DefaultAuthenticationTypes.ExternalBearer);

                        // ClaimsIdentityに、その他、所定のClaimを追加する。
                        OAuth2ProviderHelper.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, OAuth2ProviderHelper.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();
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Authorization Code、Resource Owner Password Credentialsl、Client Credentialsグラント種別において、
        /// OAuthBearerTokenEndpointPathを処理する場合に発生する、" クライアント認証 " を行なうメソッド。
        /// " クライアント認証 "では、以下の両方の要素を検証する。
        /// ・context.ClientId が、登録された "client_id" であること。
        /// ・その他、資格情報が要求に存在していることを検証する。
        /// </summary>
        /// <param name="context">OAuthValidateClientAuthenticationContext</param>
        /// <returns>Task</returns>
        /// <see cref="https://msdn.microsoft.com/ja-jp/library/dn385497.aspx"/>
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            // クライアント識別子
            string clientId     = "";
            string clientSecret = "";

            // ・context.Validated を呼び出し、contextを検証完了に設定する。
            // ・検証完了にしなければ要求はそれ以上先には進まない。
            //context.Validated(clientId);

            #region クライアント認証を行なう。

            if (string.IsNullOrEmpty(context.Parameters["grant_type"]))
            {
                // 指定なし。
                // 検証未完
            }
            else if (context.Parameters["grant_type"].ToLower() == "authorization_code")
            {
                #region Authorization Codeグラント種別

                // "client_id" および "client_secret"を基本認証の認証ヘッダから取得
                if (context.TryGetBasicCredentials(out clientId, out clientSecret))
                {
                    if ((string.IsNullOrEmpty(clientId) && string.IsNullOrEmpty(clientSecret)) == false)
                    {
                        // *.config or OAuth2Dataテーブルを参照してクライアント認証を行なう。
                        if (clientSecret == OAuth2ProviderHelper.GetInstance().GetClientSecret(context.ClientId))
                        {
                            // 検証完了
                            context.Validated(clientId);
                        }
                        else
                        {
                            // 検証未完
                        }
                    }
                }

                #endregion
            }
            else if (context.Parameters["grant_type"].ToLower() == "password")
            {
                #region Resource Owner Password Credentialsグラント種別

                #region 参考
                // Simple OAuth Server: Implementing a Simple OAuth Server with Katana
                // OAuth Authorization Server Components (Part 1) - Tugberk Ugurlu's Blog
                // http://www.tugberkugurlu.com/archive/simple-oauth-server-implementing-a-simple-oauth-server-with-katana-oauth-authorization-server-components-part-1
                // ・・・ 基本認証を使用する既存のクライアントを認証してOAuthに移行する。
                #endregion

                // "client_id" および "client_secret"を基本認証の認証ヘッダから取得
                if (context.TryGetBasicCredentials(out clientId, out clientSecret))
                {
                    if ((string.IsNullOrEmpty(clientId) && string.IsNullOrEmpty(clientSecret)) == false)
                    {
                        // *.config or OAuth2Dataテーブルを参照してクライアント認証を行なう。
                        if (clientSecret == OAuth2ProviderHelper.GetInstance().GetClientSecret(context.ClientId))
                        {
                            // 検証完了
                            context.Validated(clientId);
                        }
                        else
                        {
                            // 検証未完
                        }
                    }
                }

                #endregion
            }
            else if (context.Parameters["grant_type"].ToLower() == "client_credentials")
            {
                #region Client Credentialsグラント種別

                // "client_id" および "client_secret"を基本認証の認証ヘッダから取得
                if (context.TryGetBasicCredentials(out clientId, out clientSecret))
                {
                    if (string.IsNullOrEmpty(clientId) && string.IsNullOrEmpty(clientSecret) == false)
                    {
                        // *.config or OAuth2Dataテーブルを参照してクライアント認証を行なう。
                        if (clientSecret == OAuth2ProviderHelper.GetInstance().GetClientSecret(context.ClientId))
                        {
                            // 検証完了
                            context.Validated(clientId);
                        }
                        else
                        {
                            // 検証未完
                        }
                    }
                }

                #endregion
            }
            else if (context.Parameters["grant_type"].ToLower() == "refresh_token")
            {
                // refresh_tokenでは、不要。
                //if (context.TryGetBasicCredentials(out clientId, out clientSecret))
                //{
                //}

                if (!ASPNETIdentityConfig.EnableRefreshToken)
                {
                    throw new NotSupportedException(Resources.ApplicationOAuthBearerTokenProvider.EnableRefreshToken);
                }

                // 検証完了
                context.Validated();
            }
            else
            {
                // 不明な値
                // 検証未完
            }

            #endregion

            // 結果を返す。
            return(Task.FromResult(0));
        }