Ejemplo n.º 1
0
        /// <summary>Flush</summary>
        public override void Flush()
        {
            byte[] bb = TempBuffer.GetBuffer();

            if (bb != null && bb.Length > 0)
            {
                // 書き換え処理
                Encoding enc     = Response.ContentEncoding;
                string   content = enc.GetString(bb);

                // JSON形式なので、JsonConvertでaccess_tokenを抜き出す。
                Dictionary <string, object> accessTokenResponse = JsonConvert.DeserializeObject <Dictionary <string, object> >(content);

                // access_tokenを
                if (accessTokenResponse.ContainsKey("access_token"))
                {
                    string access_token = (string)accessTokenResponse["access_token"];
                    string id_token     = OidcTokenEditor.ChangeToIdTokenFromAccessToken(access_token, "", HashClaimType.None);
                    if (!string.IsNullOrEmpty(id_token))
                    {
                        // responseにid_tokenとして、このJWTを追加する。
                        accessTokenResponse.Add("id_token", id_token);
                        bb = enc.GetBytes(JsonConvert.SerializeObject(accessTokenResponse));
                    }
                }
            }

            ResponseStream.Write(bb, 0, bb.Length);
            ResponseStream.Flush();
        }
        /// <summary>OnPreSendRequestHeaders</summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void OnPreSendRequestHeaders(object sender, EventArgs e)
        {
            // PreSendRequestHeadersのロジックはここに挿入

            #region  数

            #region Context
            HttpApplication application = (HttpApplication)sender;
            HttpContext     context     = application.Context;
            HttpResponse    response    = context.Response;
            #endregion

            #region ワーク

            // code
            string code = "";
            // state
            string state = "";
            // expires_in
            ulong expires_in = 0;
            // redirect_uri
            string redirect_url = "";
            // access_token
            string access_token = "";
            // id_token
            string id_token = "";

            #endregion

            #endregion

            if (context.Request.Url.AbsolutePath.IndexOf(ASPNETIdentityConfig.OAuthAuthorizeEndpoint2) == -1 &&
                context.Request.Url.AbsolutePath.IndexOf(ASPNETIdentityConfig.OAuthAuthorizeEndpoint) != -1)
            {
                #region response_type

                if (this.RewritedResponseTypeFrom_IdToken ||
                    this.RewritedResponseTypeFrom_IdTokenToken)
                {
                    // OpenID Connect : Implicit Flowに対応
                    //                  - [response_type=id_token]
                    //                  - or [response_type=id_token token]

                    // レスポンス内容を参照して書き換え(fragmentにid_tokenを追加)
                    string location = response.Headers["Location"];

                    if (!string.IsNullOrEmpty(location) &&
                        location.IndexOf("#access_token=") != -1)
                    {
                        // ・正規表現でaccess_tokenを抜き出す。
                        string pattern = "(\\#access_token=)(?<accessToken>.+?)(\\&)";
                        access_token = Regex.Match(location, pattern).Groups["accessToken"].Value;

                        // at_hashを付与
                        id_token = OidcTokenEditor.ChangeToIdTokenFromAccessToken(access_token, "", HashClaimType.AtHash);

                        if (!string.IsNullOrEmpty(id_token))
                        {
                            // responseにid_tokenとして、このJWTを追加する。
                            if (this.RewritedResponseTypeFrom_IdTokenToken)
                            {
                                response.Headers["Location"] = location + "&id_token=" + id_token;
                            }
                            else if (this.RewritedResponseTypeFrom_IdToken)
                            {
                                // ココは未サポート状態なので、テストできていない。
                                location = location.Replace("access_token=" + access_token + "&", "");
                                location = location.Replace("&token_type=beara", "");
                                response.Headers["Location"] = location + "&id_token=" + id_token;
                            }
                        }
                    }
                }
                else if (this.RewritedResponseTypeFrom_CodeIdToken ||
                         this.RewritedResponseTypeFrom_CodeToken ||
                         this.RewritedResponseTypeFrom_CodeIdTokenToken)
                {
                    // OpenID Connect : Hybrid Flowに対応
                    //                  - [response_type=code id_token]
                    //                  - or [response_type=code token]
                    //                  - or [response_type=code id_token token]

                    //レスポンス内容を参照して書き換え(RedirectをFragmentに変更)
                    if (response.StatusCode == 302) // 302 Found
                    {
                        // ・Rewriteした為か、
                        //   何故か、以下が、
                        //     "?code=code値&state=state値&redirect_uri=Urlエンコードされたredirect_uri値"
                        //   となっている。
                        //   ※ redirect_uriがlocationのQueryStringに混じる。
                        //
                        // ・本来は、
                        //     "http(s)://redirect_uri値?code=code値&state=state値"
                        //   を期待していた。
                        string location = response.Headers["Location"];

                        string paramStrCode        = "?code=";
                        string paramStrState       = "&state=";
                        string paramStrRedirectUri = "&redirect_uri=";

                        if (location.IndexOf(paramStrCode) != -1 ||
                            location.IndexOf(paramStrState) != -1 ||
                            location.IndexOf(paramStrRedirectUri) != -1)
                        {
                            response.Headers.Remove("Location");

                            // 以下は、"?code=XXX&state=YYY&" という並びが前提。
                            MatchCollection matches = StringChecker.Matches(
                                location,
                                "\\?code=(?<code>.+)&state=(?<state>.+)");

                            foreach (Match match in matches)
                            {
                                GroupCollection groups = match.Groups;
                                code  = groups["code"].Value;
                                state = groups["state"].Value;
                            }

                            redirect_url = location.Substring(0, location.IndexOf('?'));

                            // ★ Hybrid Flow対応なので、expを短縮してもイイ。
                            expires_in = ulong.Parse(ASPNETIdentityConfig.OAuthAccessTokenExpireTimeSpanFromMinutes.TotalSeconds.ToString());

                            // Fragmentに組み込む
                            string fragment = "";

                            if (this.RewritedResponseTypeFrom_CodeIdToken)
                            {
                                // id_tokenを取得
                                string access_token_payload = AuthorizationCodeProvider.GetInstance().GetAccessTokenPayload(code);

                                // c_hashを付与
                                id_token = OidcTokenEditor.ChangeToIdTokenFromAccessToken(
                                    OidcTokenEditor.ProtectFromPayload(access_token_payload, expires_in), code, HashClaimType.CHash);

                                fragment = "#id_token={0}&token_type=Bearer&code={1}&expires_in={2}&state={3}";
                                fragment = string.Format(fragment, new object[] { id_token, code, expires_in, state });
                            }
                            else if (this.RewritedResponseTypeFrom_CodeToken)
                            {
                                // access_tokenを取得
                                string access_token_payload = AuthorizationCodeProvider.GetInstance().GetAccessTokenPayload(code);
                                access_token = OidcTokenEditor.ProtectFromPayload(access_token_payload, expires_in);

                                fragment = "#access_token={0}&token_type=Bearer&code={1}&expires_in={2}&state={3}";
                                fragment = string.Format(fragment, new object[] { access_token, code, expires_in, state });
                            }
                            else if (this.RewritedResponseTypeFrom_CodeIdTokenToken)
                            {
                                // id_token, access_tokenを取得
                                string access_token_payload = AuthorizationCodeProvider.GetInstance().GetAccessTokenPayload(code);
                                access_token = OidcTokenEditor.ProtectFromPayload(access_token_payload, expires_in);

                                // at_hash, c_hashの両方を付与
                                id_token = OidcTokenEditor.ChangeToIdTokenFromAccessToken(access_token, code, HashClaimType.Both);

                                fragment = "#access_token={0}&id_token={1}&token_type=Bearer&code={2}&expires_in={3}&state={4}";
                                fragment = string.Format(fragment, new object[] { access_token, id_token, code, expires_in, state });
                            }

                            // Locationを追加(redirect_url + fragment)。
                            response.Headers.Add("Location", redirect_url + fragment);
                        }
                    }
                }

                #endregion

                #region response_mode

                if (this.RewritedResponseModeFrom_FromPost)
                {
                    // OAuth2.0, OpenID Connect : response_mode=form_postに対応

                    //レスポンス内容を参照して書き換え
                    //(Redirect(get)をAuto-Submit Form(post)に変更)
                    if (response.StatusCode == 302) // 302 Found
                    {
                        // ・Rewriteした為か、
                        //   何故か、以下が、
                        //     "?code=code値&state=state値&redirect_uri=Urlエンコードされたredirect_uri値"
                        //   となっている。
                        //   ※ redirect_uriがlocationのQueryStringに混じる。
                        //
                        // ・本来は、
                        //     "http(s)://redirect_uri値?code=code値&state=state値"
                        //   を期待していた。
                        string location = response.Headers["Location"];

                        string paramStrCode        = "?code=";
                        string paramStrState       = "&state=";
                        string paramStrRedirectUri = "&redirect_uri=";

                        if (location.IndexOf(paramStrCode) != -1 ||
                            location.IndexOf(paramStrState) != -1 ||
                            location.IndexOf(paramStrRedirectUri) != -1)
                        {
                            // 302 Found ---> 200 OK
                            response.StatusCode = 200;
                            response.Headers.Remove("Location");

                            // 以下は、"?code=XXX&state=YYY&redirect_uri=ZZZ" という並びが前提。
                            MatchCollection matches = StringChecker.Matches(
                                location,
                                "\\?code=(?<code>.+)&state=(?<state>.+)&redirect_uri=(?<redirect_uri>.+)");

                            foreach (Match match in matches)
                            {
                                GroupCollection groups = match.Groups;
                                code         = groups["code"].Value;
                                state        = groups["state"].Value;
                                redirect_url = CustomEncode.UrlDecode(groups["redirect_uri"].Value);
                            }

                            // form_postに必要な、HTTP response message body
                            string body =
                                "<html>" +
                                "  <body onload=\"javascript: document.forms[0].submit()\">" +
                                "    <form method=\"post\" action =\"{0}\">" +
                                "      <input type=\"hidden\" name =\"code\" value =\"{1}\"/>" +
                                "      <input type=\"hidden\" name =\"state\"  value =\"{2}\"/>" +
                                "    </form>" +
                                "  </body>" +
                                "</html>";

                            // bodyに組み込んで
                            body = string.Format(body, redirect_url, code, state);

                            // HTTP Responseに書き出し
                            byte[] buffer = response.ContentEncoding.GetBytes(body);
                            response.OutputStream.Write(buffer, 0, buffer.Length);
                        }
                    }
                }

                #endregion

                if (this.RewritedResponseTypeFrom_IdToken ||
                    this.RewritedResponseTypeFrom_IdTokenToken ||
                    this.RewritedResponseTypeFrom_CodeIdToken ||
                    this.RewritedResponseTypeFrom_CodeToken ||
                    this.RewritedResponseTypeFrom_CodeIdTokenToken)
                //|| this.RewritedResponseModeFrom_FromPost)
                {
                    context.RewritePath(this.OriginalVirtualPath);
                }

                #region 再利用されるinstance memberの初期化を忘れずに!!
                //      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                this.OriginalVirtualPath = "";

                this.RewritedResponseTypeFrom_IdTokenToken = false;
                this.RewritedResponseTypeFrom_IdToken      = false;

                this.RewritedResponseTypeFrom_CodeIdToken      = false;
                this.RewritedResponseTypeFrom_CodeToken        = false;
                this.RewritedResponseTypeFrom_CodeIdTokenToken = false;

                this.RewritedResponseModeFrom_FromPost = false;

                #endregion
            }
        }