Example #1
0
        /// <summary>
        /// Verifies the token against the client ID.
        /// </summary>
        /// <param name="authState">The credential to verify.</param>
        /// <returns>The user ID that is associated with this token.</returns>
        static public String VerifyToken(IAuthorizationState authState)
        {
            // Use Tokeninfo to validate the user and the client.
            var tokeninfo_request = new Oauth2Service().Tokeninfo();

            tokeninfo_request.Access_token = authState.AccessToken;
            var tokeninfo = tokeninfo_request.Fetch();

            if (tokeninfo == null)
            {
                throw new TokenVerificationException("Error while fetching tokeninfo");
            }

            // Verify the first part of the token for authorization. On mobile clients,
            // the full string might differ, but the first part is consistent.
            Regex matcher = new Regex("(\\d+)(.*).apps.googleusercontent.com$");

            if (matcher.Match(tokeninfo.Issued_to).Groups[1].Value !=
                matcher.Match(CLIENT_ID).Groups[1].Value)
            {
                throw new TokenVerificationException("Issuer other than current client.");
            }

            return(tokeninfo.User_id);
        }
Example #2
0
        /// <summary>
        /// Verifies the token against the client ID.
        /// </summary>
        /// <param name="authState">The credential to verify.</param>
        /// <returns>The user ID that is associated with this token.</returns>
        public static String VerifyToken(IAuthorizationState authState)
        {
            // Use Tokeninfo to validate the user and the client.
            var tokeninfo_request = new Oauth2Service().Tokeninfo();
            tokeninfo_request.Access_token = authState.AccessToken;
            var tokeninfo = tokeninfo_request.Fetch();

            if (tokeninfo == null)
            {
                throw new TokenVerificationException("Error while fetching tokeninfo");
            }

            // Verify the first part of the token for authorization. On mobile clients,
            // the full string might differ, but the first part is consistent.
            Regex matcher = new Regex("(\\d+)(.*).apps.googleusercontent.com$");
            if (matcher.Match(tokeninfo.Issued_to).Groups[1].Value !=
                matcher.Match(CLIENT_ID).Groups[1].Value)
            {
                throw new TokenVerificationException("Issuer other than current client.");
            }

            return tokeninfo.User_id;
        }
        /// <summary>
        /// Processes the request based on the path.
        /// </summary>
        /// <param name="context">Contains the request and response.</param>
        public void ProcessRequest(HttpContext context)
        {
            // Redirect base path to signin.
            if (context.Request.Path.Equals("/"))
            {
                context.Response.RedirectPermanent("signin.ashx");
            }

            // This is reached when the root document is passed. Return HTML
            // using index.html as a template.
            if (context.Request.Path.Equals("/signin.ashx"))
            {
                String state = (String)context.Session["state"];

                // Store a random string in the session for verifying
                // the responses in our OAuth2 flow.
                if (state == null)
                {
                    Random random = new Random((int)DateTime.Now.Ticks);
                    StringBuilder builder = new StringBuilder();
                    for (int i = 0; i < 13; i++)
                    {
                        builder.Append(Convert.ToChar(
                                Convert.ToInt32(Math.Floor(
                                        26 * random.NextDouble() + 65))));
                    }
                    state = builder.ToString();
                    context.Session["state"] = state;
                }

                // Render the templated HTML.
                String templatedHTML = File.ReadAllText(
                     context.Server.MapPath("index.html"));
                templatedHTML = Regex.Replace(templatedHTML,
                    "[{]{2}\\s*APPLICATION_NAME\\s*[}]{2}", APP_NAME);
                templatedHTML = Regex.Replace(templatedHTML,
                    "[{]{2}\\s*CLIENT_ID\\s*[}]{2}", CLIENT_ID);
                templatedHTML = Regex.Replace(templatedHTML,
                    "[{]{2}\\s*STATE\\s*[}]{2}", state);

                context.Response.ContentType = "text/html";
                context.Response.Write(templatedHTML);
                return;
            }

            if (context.Session["authState"] == null)
            {
                // The connect action exchanges a code from the sign-in button,
                // verifies it, and creates OAuth2 credentials.
                if (context.Request.Path.Contains("/connect"))
                {
                    // Get the code from the request POST body.
                    StreamReader sr = new StreamReader(
                        context.Request.InputStream);
                    string code = sr.ReadToEnd();

                    string state = context.Request["state"];
                    string userid = context.Request["gplus_id"];

                    // Test that the request state matches the session state.
                    if (!state.Equals(context.Session["state"]))
                    {
                        context.Response.StatusCode = 401;
                        return;
                    }

                    // Manually perform the OAuth2 flow for now.
                    var authObject = ManualCodeExchanger.ExchangeCode(code);

                    // Create an authorization state from the returned token.
                    _authState = CreateState(
                        authObject.access_token, authObject.refresh_token,
                        DateTime.UtcNow,
                        DateTime.UtcNow.AddSeconds(authObject.expires_in));

                    // Use Tokeninfo to validate the user and the client.
                    var tokeninfo_request = new Oauth2Service().Tokeninfo();
                    tokeninfo_request.Access_token = _authState.AccessToken;
                    var tokeninfo = tokeninfo_request.Fetch();
                    if (userid == tokeninfo.User_id
                        && tokeninfo.Issued_to == CLIENT_ID)
                    {
                        context.Session["authState"] = _authState;
                    }
                    else
                    {
                        // The credentials did not match.
                        context.Response.StatusCode = 401;
                        return;
                    }
                }
                else
                {
                    // No cached state and we are not connecting.
                    context.Response.StatusCode = 400;
                    return;
                }
            }
            else
            {
                // Register the authenticator and construct the Plus service
                // for performing API calls on behalf of the user.
                _authState =
                    (IAuthorizationState)context.Session["authState"];
                AuthorizationServerDescription description =
                    GoogleAuthenticationServer.Description;
                var provider = new WebServerClient(description);
                provider.ClientIdentifier = CLIENT_ID;
                provider.ClientSecret = CLIENT_SECRET;
                var authenticator =
                    new OAuth2Authenticator<WebServerClient>(
                        provider,
                        GetAuthorization)
                    {
                        NoCaching = true
                    };
                ps = new PlusService(authenticator);
            }

            // Perform an authenticated API request to retrieve the list of
            // people that the user has made visible to the app.
            if (context.Request.Path.Contains("/people"))
            {
                // Get the PeopleFeed for the currently authenticated user.
                PeopleFeed pf = ps.People.List("me", PeopleResource.CollectionEnum.Visible).Fetch();

                // This JSON, representing the people feed, will later be
                // parsed by the JavaScript client.
                string jsonContent =
                    Newtonsoft.Json.JsonConvert.SerializeObject(pf);
                context.Response.ContentType = "application/json";
                context.Response.Write(jsonContent);
                return;
            }

            // Disconnect the user from the application by revoking the tokens
            // and removing all locally stored data associated with the user.
            if (context.Request.Path.Contains("/disconnect"))
            {
                // Perform a get request to the token endpoint to revoke the
                // refresh token.
                _authState =
                    (IAuthorizationState)context.Session["authState"];
                string token = (_authState.RefreshToken != null) ?
                    _authState.RefreshToken : _authState.AccessToken;

                WebRequest request = WebRequest.Create(
                    "https://accounts.google.com/o/oauth2/revoke?token=" +
                    token);

                WebResponse response = request.GetResponse();

                // Remove the cached credentials.
                context.Session["authState"] = null;

                // You could reset the state in the session but you must also
                // reset the state on the client.
                // context.Session["state"] = null;
                context.Response.Write(
                    response.GetResponseStream().ToString().ToCharArray());
                return;
            }
        }
        /// <summary>
        /// Processes the request based on the path.
        /// </summary>
        /// <param name="context">Contains the request and response.</param>
        public void ProcessRequest(HttpContext context)
        {
            // Get the code from the request POST body.
            string accessToken = context.Request.Params["access_token"];
            string idToken = context.Request.Params["id_token"];

            // Validate the ID token
            if (idToken != null)
            {
                JWTSecurityToken token = new JWTSecurityToken(idToken);
                JWTSecurityTokenHandler jwt = new JWTSecurityTokenHandler();

                // Configure validation
                Byte[][] certBytes = getCertBytes();
                for (int i = 0; i < certBytes.Length; i++)
                {
                    X509Certificate2 certificate = new X509Certificate2(certBytes[i]);
                    X509SecurityToken certToken = new X509SecurityToken(certificate);

                    // Set up token validation
                    TokenValidationParameters tvp = new TokenValidationParameters();
                    tvp.AllowedAudience = CLIENT_ID;
                    tvp.SigningToken = certToken;
                    tvp.ValidIssuer = "accounts.google.com";

                    // Enable / disable tests
                    tvp.ValidateNotBefore = false;
                    tvp.ValidateExpiration = true;
                    tvp.ValidateSignature = true;
                    tvp.ValidateIssuer = true;

                    // Account for clock skew. Look at current time when getting the message
                    // "The token is expired" in try/catch block.
                    // This is relative to GMT, for example, GMT-8 is:
                    tvp.ClockSkewInSeconds = 3600 * 13;

                    try
                    {
                        // Validate using the provider
                        ClaimsPrincipal cp = jwt.ValidateToken(token, tvp);
                        if (cp != null)
                        {
                            its.valid = true;
                            its.message = "Valid ID Token.";
                        }
                    }
                    catch (Exception e)
                    {
                        // Multiple certificates are tested.
                        if (its.valid != true)
                        {
                            its.message = "Invalid ID Token.";
                        }
                        if (e.Message.IndexOf("The token is expired") > 0)
                        {
                            // TODO: Check current time in the exception for clock skew.
                        }
                    }
                }

                // Get the Google+ id for this user from the "sub" claim.
                Claim[] claims = token.Claims.ToArray<Claim>();
                for (int i = 0; i < claims.Length; i++)
                {
                    if (claims[i].Type.Equals("sub"))
                    {
                        its.gplus_id = claims[i].Value;
                    }
                }
            }

            // Use Tokeninfo to validate the user and the client.
            var tokeninfo_request = new Oauth2Service().Tokeninfo();
            tokeninfo_request.Access_token = accessToken;

            // Use Google as a trusted provider to validate the token.
            // Invalid values, including expired tokens, return 400
            Tokeninfo tokeninfo = null;
            try
            {
                tokeninfo = tokeninfo_request.Fetch();
                if (tokeninfo.Issued_to != CLIENT_ID){
                    ats.message = "Access Token not meant for this app.";
                }else{
                    ats.valid = true;
                    ats.message = "Valid Access Token.";
                    ats.gplus_id = tokeninfo.User_id;
                }
            }
            catch (Exception stve)
            {
                ats.message = "Invalid Access Token.";
            }

            // Use the wrapper to return JSON
            token_status_wrapper tsr = new token_status_wrapper();
            tsr.id_token_status = its;
            tsr.access_token_status = ats;

            context.Response.StatusCode = 200;
            context.Response.ContentType = "text/json";
            context.Response.Write(JsonConvert.SerializeObject(tsr));
        }