public async Task <ActionResult> TestClientCredentialsFlow()
        {
            // Tokenエンドポイントにアクセス
            string aud = ASPNETIdentityConfig.OAuth2AuthorizationServerEndpointsRootURI
                         + ASPNETIdentityConfig.OAuth2BearerTokenEndpoint;

            // ClientNameから、client_id, client_secretを取得。
            string client_id     = "";
            string client_secret = "";

            if (User.Identity.IsAuthenticated)
            {
                // User Accountの場合、
                client_id     = OAuth2Helper.GetInstance().GetClientIdByName(User.Identity.Name);
                client_secret = OAuth2Helper.GetInstance().GetClientSecret(client_id);
            }
            else
            {
                // Client Accountの場合、
                client_id     = OAuth2Helper.GetInstance().GetClientIdByName("TestClient");
                client_secret = OAuth2Helper.GetInstance().GetClientSecret(client_id);
            }

            string response = await OAuth2Helper.GetInstance()
                              .ClientCredentialsGrantAsync(new Uri(
                                                               ASPNETIdentityConfig.OAuth2AuthorizationServerEndpointsRootURI
                                                               + ASPNETIdentityConfig.OAuth2BearerTokenEndpoint),
                                                           client_id, client_secret, ASPNETIdentityConst.StandardScopes);

            ViewBag.Response    = response;
            ViewBag.AccessToken = ((JObject)JsonConvert.DeserializeObject(response))[OAuth2AndOIDCConst.AccessToken];

            return(View("OAuth2ClientAuthenticationFlow"));
        }
        /// <summary>初期化</summary>
        private void Init()
        {
            this.OAuthAuthorizeEndpoint =
                ASPNETIdentityConfig.OAuth2AuthorizationServerEndpointsRootURI
                + ASPNETIdentityConfig.OAuth2AuthorizeEndpoint;

            this.ClientId = OAuth2Helper.GetInstance().GetClientIdByName("TestClient");
            this.State    = GetPassword.Generate(10, 0); // 記号は入れない。
            this.Nonce    = GetPassword.Generate(20, 0); // 記号は入れない。

            this.CodeVerifier  = "";
            this.CodeChallenge = "";
        }
        public async Task <ActionResult> TestJWTBearerTokenFlow()
        {
            // Token2エンドポイントにアクセス
            string aud = ASPNETIdentityConfig.OAuth2AuthorizationServerEndpointsRootURI
                         + ASPNETIdentityConfig.OAuth2BearerTokenEndpoint2;

            // ClientNameから、client_id(iss)を取得。
            string iss = "";

            if (User.Identity.IsAuthenticated)
            {
                // User Accountの場合、
                iss = OAuth2Helper.GetInstance().GetClientIdByName(User.Identity.Name);
            }
            else
            {
                // Client Accountの場合、
                iss = OAuth2Helper.GetInstance().GetClientIdByName("TestClient");
            }

            // テストなので秘密鍵は共通とする。
            string privateKey = OAuth2AndOIDCParams.OAuth2JwtAssertionPrivatekey;

            privateKey = CustomEncode.ByteToString(CustomEncode.FromBase64String(privateKey), CustomEncode.us_ascii);

            string response = await OAuth2Helper.GetInstance()
                              .JwtBearerTokenFlowAsync(new Uri(
                                                           ASPNETIdentityConfig.OAuth2AuthorizationServerEndpointsRootURI
                                                           + ASPNETIdentityConfig.OAuth2BearerTokenEndpoint2),
                                                       JwtAssertion.CreateJwtBearerTokenFlowAssertion(
                                                           iss, aud, new TimeSpan(0, 0, 30), ASPNETIdentityConst.StandardScopes, privateKey));

            ViewBag.Response    = response;
            ViewBag.AccessToken = ((JObject)JsonConvert.DeserializeObject(response))[OAuth2AndOIDCConst.AccessToken];

            return(View("OAuth2ClientAuthenticationFlow"));
        }
Ejemplo n.º 4
0
        public async Task <Dictionary <string, object> > GetUserClaims()
        {
            // Claim情報を参照する。
            // iss, aud, expのチェックは、AccessTokenFormatJwt.Unprotectで実施済。
            ClaimsIdentity id        = (ClaimsIdentity)User.Identity;
            Claim          claim_aud = id.FindFirst(OAuth2AndOIDCConst.Claim_Audience);

            // ユーザ認証を行なう。
            ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

            string subject = "";

            if (user == null)
            {
                // Client認証
                subject = OAuth2Helper.GetInstance().GetClientName(claim_aud.Value);
            }
            else
            {
                // Resource Owner認証
                subject = user.UserName;
            }

            Dictionary <string, object> userinfoClaimSet = new Dictionary <string, object>();

            userinfoClaimSet.Add(OAuth2AndOIDCConst.sub, subject);

            // Scope
            IEnumerable <Claim> claimScope = id.FindAll(OAuth2AndOIDCConst.Claim_Scopes).AsEnumerable();

            // scope値によって、返す値を変更する。
            foreach (Claim scope in claimScope)
            {
                if (user != null)
                {
                    // user == null では NG な Resource(Resource Owner の Resource)
                    switch (scope.Value.ToLower())
                    {
                        #region OpenID Connect

                    case OAuth2AndOIDCConst.Scope_Profile:
                        // ・・・
                        break;

                    case OAuth2AndOIDCConst.Scope_Email:
                        userinfoClaimSet.Add(OAuth2AndOIDCConst.Scope_Email, user.Email);
                        userinfoClaimSet.Add(OAuth2AndOIDCConst.email_verified, user.EmailConfirmed.ToString());
                        break;

                    case OAuth2AndOIDCConst.Scope_Phone:
                        userinfoClaimSet.Add(OAuth2AndOIDCConst.phone_number, user.PhoneNumber);
                        userinfoClaimSet.Add(OAuth2AndOIDCConst.phone_number_verified, user.PhoneNumberConfirmed.ToString());
                        break;

                    case OAuth2AndOIDCConst.Scope_Address:
                        // ・・・
                        break;

                        #endregion

                        #region Else

                    case OAuth2AndOIDCConst.Scope_UserID:
                        userinfoClaimSet.Add(OAuth2AndOIDCConst.Scope_UserID, user.Id);
                        break;

                    case OAuth2AndOIDCConst.Scope_Roles:
                        userinfoClaimSet.Add(
                            OAuth2AndOIDCConst.Scope_Roles,
                            await UserManager.GetRolesAsync(user.Id));
                        break;

                        #endregion
                    }
                }
                else
                {
                    // user == null でも OK な Resource
                }
            }

            return(userinfoClaimSet);
        }
Ejemplo n.º 5
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である必要があるとの記載あり形式でないと弾かれる。

            #region response_type

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

            // OIDC Implicit, Hybridの場合、書き換え
            if (ASPNETIdentityConfig.EnableOpenIDConnect)
            {
                if (response_type.ToLower() == OAuth2AndOIDCConst.OidcImplicit1_ResponseType ||
                    response_type.ToLower() == OAuth2AndOIDCConst.OidcImplicit2_ResponseType ||
                    response_type.ToLower() == OAuth2AndOIDCConst.OidcHybrid2_IdToken_ResponseType ||
                    response_type.ToLower() == OAuth2AndOIDCConst.OidcHybrid2_Token_ResponseType ||
                    response_type.ToLower() == OAuth2AndOIDCConst.OidcHybrid3_ResponseType)
                {
                    // OIDC Implicit, Hybridの場合、書き換え
                    // Authorization Code Flowの場合は、codeなので書き換え不要。
                    // ※ この変数は、使用するredirect_uriを決定するだめダケに利用される。
                    response_type = OAuth2AndOIDCConst.ImplicitResponseType;

                    // OIDC Implicit Flow、Hybrid Flowのパラメタチェック

                    // nonceパラメタ 必須
                    string nonce = context.Request.Query.Get(OAuth2AndOIDCConst.nonce);
                    if (string.IsNullOrEmpty(nonce))
                    {
                        //throw new NotSupportedException("there was no nonce in query.");
                        context.SetError(
                            "server_error",
                            "there was no nonce in query.");
                        return(Task.FromResult(0));
                    }

                    // scopeパラメタ 必須
                    string scope = context.Request.Query.Get(OAuth2AndOIDCConst.scope);
                    if (scope.IndexOf(OAuth2AndOIDCConst.Scope_Openid) == -1)
                    {
                        //throw new NotSupportedException("there was no openid in scope of query.");
                        context.SetError(
                            "server_error",
                            "there was no openid in scope of query.");
                        return(Task.FromResult(0));
                    }
                }
            }

            if (!string.IsNullOrEmpty(response_type))
            {
                if (response_type.ToLower() == OAuth2AndOIDCConst.AuthorizationCodeResponseType)
                {
                    if (!ASPNETIdentityConfig.EnableAuthorizationCodeGrantType)
                    {
                        //throw new NotSupportedException(Resources.ApplicationOAuthBearerTokenProvider.EnableAuthorizationCodeGrantType);
                        context.SetError(
                            "server_error",
                            Resources.ApplicationOAuthBearerTokenProvider.EnableAuthorizationCodeGrantType);
                        return(Task.FromResult(0));
                    }
                }
                else if (response_type.ToLower() == OAuth2AndOIDCConst.ImplicitResponseType)
                {
                    if (!ASPNETIdentityConfig.EnableImplicitGrantType)
                    {
                        //throw new NotSupportedException(Resources.ApplicationOAuthBearerTokenProvider.EnableImplicitGrantType);
                        context.SetError(
                            "server_error",
                            Resources.ApplicationOAuthBearerTokenProvider.EnableImplicitGrantType);
                        return(Task.FromResult(0));
                    }
                }
            }

            #endregion

            #region redirect_uri

            string redirect_uri = context.RedirectUri;

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

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

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

                // 指定されたredirect_uriを使用する場合は、チェックが必要になる。
                if (
                    // self_code : Authorization Codeグラント種別
                    redirect_uri == (ASPNETIdentityConfig.OAuth2ClientEndpointsRootURI + ASPNETIdentityConfig.OAuth2AuthorizationCodeGrantClient_Manage)
                    )
                {
                    // 不特定多数のクライアント識別子に許可されたredirect_uri
                    context.Validated(redirect_uri);
                }
                else
                {
                    // クライアント識別子に対応する事前登録したredirect_uriに
                    string preRegisteredUri = OAuth2Helper.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.º 6
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
            // 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();
            }
        }
Ejemplo n.º 7
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.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();
            }
        }
Ejemplo n.º 8
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[OAuth2AndOIDCConst.grant_type]))
            {
                // 指定なし。
                // 検証未完
            }
            else if (context.Parameters[OAuth2AndOIDCConst.grant_type].ToLower() == OAuth2AndOIDCConst.AuthorizationCodeGrantType)
            {
                #region Authorization Codeグラント種別

                // "client_id" および "client_secret"を基本認証の認証ヘッダから取得
                if (context.TryGetBasicCredentials(out clientId, out clientSecret))
                {
                    // 通常のクライアント認証
                    if (!(string.IsNullOrEmpty(clientId) && string.IsNullOrEmpty(clientSecret)))
                    {
                        // *.config or OAuth2Dataテーブルを参照してクライアント認証を行なう。
                        if (clientSecret == OAuth2Helper.GetInstance().GetClientSecret(context.ClientId))
                        {
                            // 検証完了
                            context.Validated(clientId);
                        }
                    }
                }
                else
                {
                    // その他のクライアント認証の可能性
                    string assertion = context.Parameters.Get(OAuth2AndOIDCConst.assertion);
                    if (!string.IsNullOrEmpty(assertion))
                    {
                        // JWT client assertion
                        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.OAuth2BearerTokenEndpoint)
                                {
                                    // 検証完了
                                    context.Validated(iss);
                                }
                            }
                        }
                    }
                    else
                    {
                        // クライアント認証なしエラー
                    }
                }

                #endregion
            }
            else if (context.Parameters[OAuth2AndOIDCConst.grant_type].ToLower() == OAuth2AndOIDCConst.ResourceOwnerPasswordCredentialsGrantType)
            {
                #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)))
                    {
                        // *.config or OAuth2Dataテーブルを参照してクライアント認証を行なう。
                        if (clientSecret == OAuth2Helper.GetInstance().GetClientSecret(context.ClientId))
                        {
                            // 検証完了
                            context.Validated(clientId);
                        }
                    }
                }

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

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

                #endregion
            }
            else if (context.Parameters[OAuth2AndOIDCConst.grant_type].ToLower() == OAuth2AndOIDCConst.RefreshTokenGrantType)
            {
                #region RefreshToken

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

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

                #endregion
            }
            else
            {
                // 不明な値
                // 検証未完
            }

            #endregion

            // 結果を返す。
            return(Task.FromResult(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() == ASPNETIdentityConst.AuthorizationCodeGrantType)
            {
                #region Authorization Codeグラント種別

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

                #endregion
            }
            else if (context.Parameters["grant_type"].ToLower() == ASPNETIdentityConst.ResourceOwnerPasswordCredentialsGrantType)
            {
                #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)))
                    {
                        // *.config or OAuth2Dataテーブルを参照してクライアント認証を行なう。
                        if (clientSecret == OAuth2Helper.GetInstance().GetClientSecret(context.ClientId))
                        {
                            // 検証完了
                            context.Validated(clientId);
                        }
                    }
                }

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

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

                #endregion
            }
            else if (context.Parameters["grant_type"].ToLower() == ASPNETIdentityConst.RefreshTokenGrantType)
            {
                #region RefreshToken

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

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

                #endregion
            }
            else
            {
                // 不明な値
                // 検証未完
            }

            #endregion

            // 結果を返す。
            return(Task.FromResult(0));
        }
Ejemplo n.º 10
0
        /// <summary>Protect</summary>
        /// <param name="ticket">AuthenticationTicket</param>
        /// <returns>JWT文字列</returns>
        public string Protect(AuthenticationTicket ticket)
        {
            string json = "";
            string jwt  = "";

            // チェック
            if (ticket == null)
            {
                throw new ArgumentNullException("ticket");
            }

            ApplicationUserManager userManager
                = HttpContext.Current.GetOwinContext().GetUserManager <ApplicationUserManager>();

            ApplicationUser user = null;

            if (ticket.Identity.Name == null)
            {
                // Client認証の場合、
                user = null;
            }
            else
            {
                // Resource Owner認証の場合、
                user = userManager.FindByName(ticket.Identity.Name); // 同期版でOK。
            }

            #region ClaimSetの生成

            Dictionary <string, object> authTokenClaimSet = new Dictionary <string, object>();
            List <string> scopes = new List <string>();
            List <string> roles  = new List <string>();

            foreach (Claim c in ticket.Identity.Claims)
            {
                if (c.Type == ASPNETIdentityConst.Claim_Issuer)
                {
                    authTokenClaimSet.Add("iss", c.Value);
                }
                else if (c.Type == ASPNETIdentityConst.Claim_Audience)
                {
                    authTokenClaimSet.Add("aud", c.Value);
                }
                else if (c.Type == ASPNETIdentityConst.Claim_Nonce)
                {
                    authTokenClaimSet.Add("nonce", c.Value);
                }
                else if (c.Type == ASPNETIdentityConst.Claim_Scope)
                {
                    scopes.Add(c.Value);
                }
                else if (c.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role")
                {
                    roles.Add(c.Value);
                }
            }

            if (ticket.Identity.Name == null)
            {
                // Client認証の場合、aud(client_id)に対応するClient名称
                authTokenClaimSet.Add("sub",
                                      OAuth2Helper.GetInstance().GetClientName((string)authTokenClaimSet["aud"]));
            }
            else
            {
                // Resource Owner認証の場合、Resource Ownerの名称
                authTokenClaimSet.Add("sub", ticket.Identity.Name);
            }

            authTokenClaimSet.Add("exp", ticket.Properties.ExpiresUtc.Value.ToUnixTimeSeconds().ToString());
            authTokenClaimSet.Add("nbf", DateTimeOffset.Now.ToUnixTimeSeconds().ToString());
            authTokenClaimSet.Add("iat", ticket.Properties.IssuedUtc.Value.ToUnixTimeSeconds().ToString());
            authTokenClaimSet.Add("jti", Guid.NewGuid().ToString("N"));

            authTokenClaimSet.Add("scopes", scopes);

            // scope値によって、返す値を変更する。
            foreach (string scope in scopes)
            {
                if (user != null)
                {
                    // user == null では NG な Resource(Resource Owner の Resource)
                    switch (scope.ToLower())
                    {
                        #region OpenID Connect

                    case ASPNETIdentityConst.Scope_Profile:
                        // ・・・
                        break;

                    case ASPNETIdentityConst.Scope_Email:
                        authTokenClaimSet.Add("email", user.Email);
                        authTokenClaimSet.Add("email_verified", user.EmailConfirmed.ToString());
                        break;

                    case ASPNETIdentityConst.Scope_Phone:
                        authTokenClaimSet.Add("phone_number", user.PhoneNumber);
                        authTokenClaimSet.Add("phone_number_verified", user.PhoneNumberConfirmed.ToString());
                        break;

                    case ASPNETIdentityConst.Scope_Address:
                        // ・・・
                        break;

                        #endregion

                        #region Else

                    case ASPNETIdentityConst.Scope_Userid:
                        authTokenClaimSet.Add(ASPNETIdentityConst.Scope_Userid, user.Id);
                        break;

                    case ASPNETIdentityConst.Scope_Roles:
                        authTokenClaimSet.Add(
                            ASPNETIdentityConst.Scope_Roles,
                            userManager.GetRolesAsync(user.Id).Result);
                        break;

                        #endregion
                    }
                }
                else
                {
                    // user == null でも OK な Resource
                }
            }

            json = JsonConvert.SerializeObject(authTokenClaimSet);

            #endregion

            #region JWT化

            JWT_RS256 jwtRS256 = null;

            // 署名
            jwtRS256 = new JWT_RS256(ASPNETIdentityConfig.OAuthJWT_pfx, ASPNETIdentityConfig.OAuthJWTPassword,
                                     X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);

            jwt = jwtRS256.Create(json);

            // 検証
            jwtRS256 = new JWT_RS256(ASPNETIdentityConfig.OAuthJWT_cer, ASPNETIdentityConfig.OAuthJWTPassword,
                                     X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);

            if (jwtRS256.Verify(jwt))
            {
                return(jwt); // 検証できた。
            }
            else
            {
                return(""); // 検証できなかった。
            }

            #endregion
        }
Ejemplo n.º 11
0
        /// <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);
        }
        /// <summary>Protect</summary>
        /// <param name="ticket">AuthenticationTicket</param>
        /// <returns>JWT文字列</returns>
        public string Protect(AuthenticationTicket ticket)
        {
            string json = "";

            //string jws = "";

            // チェック
            if (ticket == null)
            {
                throw new ArgumentNullException("ticket");
            }

            ApplicationUserManager userManager
                = HttpContext.Current.GetOwinContext().GetUserManager <ApplicationUserManager>();

            ApplicationUser user = null;

            if (ticket.Identity.Name == null)
            {
                // Client認証の場合、
                user = null;
            }
            else
            {
                // Resource Owner認証の場合、
                user = userManager.FindByName(ticket.Identity.Name); // 同期版でOK。
            }

            #region ClaimSetの生成

            Dictionary <string, object> authTokenClaimSet = new Dictionary <string, object>();
            List <string> scopes = new List <string>();
            List <string> roles  = new List <string>();

            foreach (Claim c in ticket.Identity.Claims)
            {
                if (c.Type == OAuth2AndOIDCConst.Claim_Issuer)
                {
                    authTokenClaimSet.Add(OAuth2AndOIDCConst.iss, c.Value);
                }
                else if (c.Type == OAuth2AndOIDCConst.Claim_Audience)
                {
                    authTokenClaimSet.Add(OAuth2AndOIDCConst.aud, c.Value);
                }
                else if (c.Type == OAuth2AndOIDCConst.Claim_Nonce)
                {
                    authTokenClaimSet.Add(OAuth2AndOIDCConst.nonce, c.Value);
                }
                else if (c.Type == OAuth2AndOIDCConst.Claim_Scopes)
                {
                    scopes.Add(c.Value);
                }
                else if (c.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role")
                {
                    roles.Add(c.Value);
                }
            }

            if (ticket.Identity.Name == null)
            {
                // Client認証の場合、aud(client_id)に対応するClient名称
                authTokenClaimSet.Add(OAuth2AndOIDCConst.sub,
                                      OAuth2Helper.GetInstance().GetClientName((string)authTokenClaimSet[OAuth2AndOIDCConst.aud]));
            }
            else
            {
                // Resource Owner認証の場合、Resource Ownerの名称
                authTokenClaimSet.Add(OAuth2AndOIDCConst.sub, ticket.Identity.Name);
            }

            authTokenClaimSet.Add(OAuth2AndOIDCConst.exp, ticket.Properties.ExpiresUtc.Value.ToUnixTimeSeconds().ToString());
            authTokenClaimSet.Add(OAuth2AndOIDCConst.nbf, DateTimeOffset.Now.ToUnixTimeSeconds().ToString());
            authTokenClaimSet.Add(OAuth2AndOIDCConst.iat, ticket.Properties.IssuedUtc.Value.ToUnixTimeSeconds().ToString());
            authTokenClaimSet.Add(OAuth2AndOIDCConst.jti, Guid.NewGuid().ToString("N"));

            authTokenClaimSet.Add(OAuth2AndOIDCConst.scopes, scopes);

            // scope値によって、返す値を変更する。
            foreach (string scope in scopes)
            {
                if (user != null)
                {
                    // user == null では NG な Resource(Resource Owner の Resource)
                    switch (scope.ToLower())
                    {
                        #region OpenID Connect

                    case OAuth2AndOIDCConst.Scope_Profile:
                        // ・・・
                        break;

                    case OAuth2AndOIDCConst.Scope_Email:
                        authTokenClaimSet.Add(OAuth2AndOIDCConst.Scope_Email, user.Email);
                        authTokenClaimSet.Add(OAuth2AndOIDCConst.email_verified, user.EmailConfirmed.ToString());
                        break;

                    case OAuth2AndOIDCConst.Scope_Phone:
                        authTokenClaimSet.Add(OAuth2AndOIDCConst.phone_number, user.PhoneNumber);
                        authTokenClaimSet.Add(OAuth2AndOIDCConst.phone_number_verified, user.PhoneNumberConfirmed.ToString());
                        break;

                    case OAuth2AndOIDCConst.Scope_Address:
                        // ・・・
                        break;

                        #endregion

                        #region Else

                    case OAuth2AndOIDCConst.Scope_UserID:
                        authTokenClaimSet.Add(OAuth2AndOIDCConst.Scope_UserID, user.Id);
                        break;

                    case OAuth2AndOIDCConst.Scope_Roles:
                        authTokenClaimSet.Add(
                            OAuth2AndOIDCConst.Scope_Roles,
                            userManager.GetRolesAsync(user.Id).Result);
                        break;

                        #endregion
                    }
                }
                else
                {
                    // user == null でも OK な Resource
                }
            }

            json = JsonConvert.SerializeObject(authTokenClaimSet);

            #endregion

            #region JWS化

            JWS_RS256_X509 jwsRS256 = null;

            // JWT_RS256_X509
            jwsRS256 = new JWS_RS256_X509(ASPNETIdentityConfig.OAuth2JWT_pfx, ASPNETIdentityConfig.OAuth2JWTPassword,
                                          X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);

            // JWSHeaderのセット
            // kid : https://openid-foundation-japan.github.io/rfc7638.ja.html#Example
            Dictionary <string, string> jwk =
                JsonConvert.DeserializeObject <Dictionary <string, string> >(
                    RS256_KeyConverter.X509PfxToJwkPublicKey(ASPNETIdentityConfig.OAuth2JWT_pfx, ASPNETIdentityConfig.OAuth2JWTPassword));

            jwsRS256.JWSHeader.kid = jwk[JwtConst.kid];
            jwsRS256.JWSHeader.jku = ASPNETIdentityConfig.OAuth2AuthorizationServerEndpointsRootURI + OAuth2AndOIDCParams.JwkSetUri;

            // 署名
            return(jwsRS256.Create(json));

            //// 検証
            //jwsRS256 = new JWS_RS256_X509(OAuth2AndOIDCParams.RS256Cer, ASPNETIdentityConfig.OAuth2JWTPassword,
            //    X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);

            //if (jwsRS256.Verify(jws))
            //{
            //    return jws; // 検証できた。
            //}
            //else
            //{
            //    return ""; // 検証できなかった。
            //}

            #endregion
        }