How to store access token? (Oauth 2, Auth code flow)

58,963

Solution 1

The client, in OAuth terminology, is the component that makes requests to the resource server, in your case, the client is the server of a web application (NOT the browser).

Therefore, the access token should be stored on the web application server only. It should not be exposed to the browser, and it doesn't need to, because the browser never makes any direct requests to the resource server. It talks to the web application server instead, which in turn makes requests to the resource server using the access token.

How the browser authenticates itself with the web application server has nothing to do with OAuth 2.0. For example, it might be a regular session cookie, and the web application server might associate each session or each user with an access token.

The token request, which exchanges the authentication code for an access token, is done by the web application server, and the web application server should authenticate itself with the authorization server (e.g., using a shared client_secret).

Authorization code flow ensures that the client can be authenticated, which protects against malicious clients posing as legitimate clients. Not all web application clients have a server component, and in some cases, requests to the resource server are made directly by JavaScript code in the browser. In such situations, the browser is the client, and the access token must be stored by the browser (in a JavaScript variable, local storage or a cookie). In this case, the client cannot be authenticated (but a reasonable amount of security may be achieved by using TLS and the server redirecting only to registered endpoint URLs).

Recommended reading regarding OAuth 2.0 security: https://www.rfc-editor.org/rfc/rfc6819#section-4.3.3 (RFC 6819)

Solution 2

The cookie is never exposed to the browser. It is part of the response returned from the authorization server to the client, which itself is a server, not a browser. The CallbackController, which implements the redirection endpoint, extracts the cookie from the response.

The cookie is never passed on to the browser. How the browser authenticates itself with the client's application server is not shown in your sample code, and it is not part of OAuth.

The authorization server could store the token in the request body (e.g., in JSON format) rather than in a cookie. However, this makes no difference, because the client can see and process the entire response anyway.

For details, see my other answer: https://stackoverflow.com/a/44655679/2279059

Side note: The CallbackController uses state to store a final URL to redirect the browser to. This is non-standard but works. However, state is actually meant to protect the redirection endpoints against CSRF attacks. The CallbackController does not validate state but blindly redirects to whatever URL was given. Probably this detail was left out, because the code was meant as an example. However, it shows that this code is probably not entirely production-ready.

Solution 3

If you are going to request a rest resource from the browser, the flow you need is Implicit Grant. Check this Auth0 post to decide between the flows https://auth0.com/docs/api-auth/which-oauth-flow-to-use. If you want to use the access token from server you should store the Authorization code and generate an access token every time you need it, access token is not intended to live more than 5 minutes, you don't need to store it

Share:
58,963
BodzioSamolot
Author by

BodzioSamolot

Updated on May 04, 2020

Comments

  • BodzioSamolot
    BodzioSamolot about 4 years

    From what i understand the purpose of the Authorization Code flow is to exchange the auth code for access token. This exchange happens between the server which serves the page and authorization server so that the actual access token is not exposed to the client user.

    How should the page server store the access token once it is obtained? I was learning from a Pluralsight example in which there is this part of code:

        public static HttpClient GetClient()
        {
            HttpClient client = new HttpClient();
            var accessToken = RequestAccessTokenAuthorizationCode();
            client.SetBearerToken(accessToken);
    
            client.BaseAddress = new Uri(IdentityConstants.API);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
    
            return client;
        }
    
        private static string RequestAccessTokenAuthorizationCode()
        {
            // did we store the token before?
            var cookie = HttpContext.Current.Request.Cookies.Get("ClientMVCCookie.AuthCode");
            if (cookie != null && cookie["access_token"] != null && !string.IsNullOrEmpty(cookie["access_token"]))
            {
                return cookie["access_token"];
            }
    
            // no token found - request one
    
            // we'll pass through the URI we want to return to as state
            var state = HttpContext.Current.Request.Url.OriginalString;
    
            var authorizeRequest = new IdentityModel.Client.AuthorizeRequest(
                IdentityConstants.AuthEndoint);
    
            var url = authorizeRequest.CreateAuthorizeUrl(IdentityConstants.MVCClientSecret, "code", "management secret",
                IdentityConstants.MVCAuthCodeCallback, state);
    
            HttpContext.Current.Response.Redirect(url);
    
            return null;
        }
    }
    

    This will cause each request to check if there is an access token stored in the cookie. If not then the flow will be initiated. The callback looks like this:

    public class CallbackController : Controller
    {
        // GET: STSCallback
        public async Task<ActionResult> Index()
        {
            // get the authorization code from the query string
            var authCode = Request.QueryString["code"];
    
            // with the auth code, we can request an access token.
            var client = new TokenClient(
                IdentityConstants.TokenEndoint,
                "mvc_client_auth_code",
                 IdentityConstants.MVCClientSecretAuthCode);
    
            var tokenResponse = await client.RequestAuthorizationCodeAsync(
                authCode,
                IdentityConstants.MVCAuthCodeCallback);
    
            // we save the token in a cookie for use later on
            var cookie = Response.Cookies["ClientMVCCookie.AuthCode"];
            cookie.Expires = DateTime.Now.AddMinutes(1);
            cookie["access_token"] = tokenResponse.AccessToken;
    
            // get the state (uri to return to)
            var state = Request.QueryString["state"];
    
            // redirect to the URI saved in state
            return Redirect(state);
        }
    }
    

    Doesn't storing the access token in the cookie defeath the whole purpose of the authorization code flow? The cookie will be transmitted to the client browser thus exposing it to the client? Am i missing something? It this is not the correct way to store the token, how should it be stored?