OpenID Connect Hybrid Flow and IdentityServer v3

One of the features we added in Beta 2 is support for hybrid flow (see spec).  What is hybrid flow – and why do I care?

Well – in a nutshell – OpenID Connect originally extended the two basic OAuth2 flows (or grants) called authorization code and implicit. Implicit allows requesting tokens without explicit client authentication (hence the name), but uses the redirect URI instead to verify client identity. Because of that, requesting long lived tokens like a refresh token is not allowed in that flow.

Authorization code flow on the other hand only returns a so called authorization code via the unauthenticated front channel, and requires client authentication using client id and secret (or some other mechanism) to retrieve the actual tokens (including a refresh token) via the back channel. This mechanism was originally designed for server-based applications only, since storing client secrets on a client device is questionable without the right security mechanisms in place.

Hybrid flow (as the name indicates) is a combination of the above two. It allows to request a combination of identity token, access token and code via the front channel using either a fragment encoded redirect (native and JS based clients) or a form post (server-based web applications). This enables e.g. scenarios where your client app can make immediate use of an identity token to get access to the user’s identity but also retrieve an authorization code that that can be used (e.g. by a back end service) to request a refresh token and thus gaining long lived access to resources.

Lastly, hybrid flow is the only flow supported by the Microsoft OpenID Connect authentication middleware (in combination with a form post response mode), and before we added support for hybrid flow to IdentityServer, interop was a bit complicated (see here).

Our samples repo has two clients using hybrid flow – native and web. Let’s have a look at the middleware based one.

You are using hybrid flow whenever you have a response type of code combined with some other response type, e.g. id_token or token (or both).

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions

    {

        ClientId = “katanaclient”,

        Authority = Constants.BaseAddress,

        RedirectUri = Constants.RedirectUri,

        PostLogoutRedirectUri = Constants.PostLogoutUri,

        ResponseType = “code id_token token”,

        Scope = “openid email profile read write offline_access”,

 

        SignInAsAuthenticationType = “Cookies”

    };

 

The scopes are a combination of identity scopes allowing us to retrieve user identity (email and profile), resource scopes for api access (read and write) and a request for a refresh token (offline_access).

After authentication (and consent), the middleware will validate the response and notify you when it has retrieved the authorization code from the callback form post. You then typically have a number of responsibilities, e.g.

  • Contact the userinfo endpoint to retrieve the claims about the user
  • Transform the claims to whatever format your application expects
  • Store the access token for later use (along with information about its lifetime)
  • Store the refresh token so you can refresh expired access tokens (if long lived access is needed)
  • Store the id_token if you need features at the OpenID Connect provider that requires id token hints (e.g. redirects after logging out)

In our sample, I first strip all the protocol related claims from the identity token:

// filter "protocol" claims
var claims = new List<Claim>(from c in n.AuthenticationTicket.Identity.Claims
                                where c.Type != "iss" &&
                                      c.Type != "aud" &&
                                      c.Type != "nbf" &&
                                      c.Type != "exp" &&
                                      c.Type != "iat" &&
                                      c.Type != "nonce" &&
                                      c.Type != "c_hash" &&
                                      c.Type != "at_hash"
                                select c);

 

…then get the user claims from the user info endpoint

// get userinfo data
var userInfoClient = new UserInfoClient(
    new Uri(Constants.UserInfoEndpoint),
    n.ProtocolMessage.AccessToken);
 
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => claims.Add(new Claim(ui.Item1, ui.Item2)));

 

…and retrieve a refresh token (and a fresh access token)

// get access and refresh token
var tokenClient = new OAuth2Client(
    new Uri(Constants.TokenEndpoint),
    "katanaclient",
    "secret");
 
var response = await tokenClient.RequestAuthorizationCodeAsync(
    n.Code, n.RedirectUri);

 

Then I combine all the claims together and store them in the authentication cookie:

claims.Add(new Claim("access_token", response.AccessToken));
claims.Add(new Claim("expires_at", 
    DateTime.Now.AddSeconds(response.ExpiresIn).ToLocalTime().ToString()));
claims.Add(new Claim("refresh_token", response.RefreshToken));
claims.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
 
n.AuthenticationTicket = new AuthenticationTicket(
    new ClaimsIdentity(claims.Distinct(new ClaimComparer()), 
        n.AuthenticationTicket.Identity.AuthenticationType), 
        n.AuthenticationTicket.Properties);

 

You might not need all of the above steps, but this is how it generally works. HTH.

This entry was posted in IdentityServer, Katana, OAuth, OpenID Connect, OWIN, WebAPI. Bookmark the permalink.

33 Responses to OpenID Connect Hybrid Flow and IdentityServer v3

  1. Pingback: OpenID Connect Endpoints | Scott Brady

  2. Pingback: OpenID Connect Flows | Scott Brady

  3. Alexey Auslender says:

    One point that I didn’t understand ,why should we request access token and authorization code once again ,nevertheless we requested it through OpenIdConnectAuthenticationOptions configuration.I mean it was possible to retrieve it in such way :
    claims.Add(new Claim(“access_token”, n.ProtocolMessage.AccessToken));
    claims.Add(new Claim(“expires_at”,DateTime.Now.AddSeconds(Double.Parse(n.ProtocolMessage.ExpiresIn)).ToLocalTime().ToString()));
    claims.Add(new Claim(“refresh_token”, n.Code));
    claims.Add(new Claim(“id_token”, n.ProtocolMessage.IdToken));
    Why should we retrieve it separately through the same client id (katanaclient)?

  4. RonyK says:

    Assuming I’m implementing my RP as above, and my client is an SPA accessing the protected resource server (API), What would be the best way to implement the access_token life time management and the use of refresh token to obtain new access_tokens?

    • Refresh tokens are not really designed for SPAs. They are for server side applications. I would rather use long lived reference tokens.

      • RonyK says:

        Sorry few questions.
        What do you mean by “reference tokens”? Are you referring the access_token?
        Aren’t long lived tokens unsecured?
        How long would be wise to set duration of the long lived token?

  5. Reference tokens are access tokens that can be revoked.

  6. Vishal Vyas says:

    When user logs in by entering username and password lets say if application just wants to grab id_token at that time and later on, based on what kind of actions user performs if application determines that it needs to call a web API and now wants access token for that API (on behalf of user) is it possible using identity server 3 to get access token by passing id_token and not prompting user again for username and password something like (assertion flow kind of thing) ?

    • You don’t need to prompt the user again since the SSO session is already established. But you need to make a (front channel) roundtrip to the authorize endpoint again.

  7. RonyK says:

    As far as I can tell The MvcCodeFlow sample is not exactly what I want to do. It calls the API from the back channel and I want to call it directly from the front channel.

    Would this be a valid approach : Using code flow, exchanging the code into a reference token in the back channel passing the reference token to the front channel and then use it there to directly call the API? If so what would be the recommended way to pass it from back to front, and what would be the best way to store it on the front?

  8. It might not be *exactly* what you are doing – but it is using a reference token. and that also works from client applications.

  9. oh and please continue any discussion on github, thanks

    • Tom says:

      Hi Dominick,
      I feel like @RonyK is asking a good question here that a ton of people have been looking for an answer to. In this post you’re hinting at something that gives people some hope that perhaps the hybrid flow has a solution for it, but then in response to the comment you seem to be suggesting that an implicit flow should be used instead.

      In this blog post, you say:
      “This enables e.g. scenarios where your client app can make immediate use of an identity token to get access to the user’s identity but also retrieve an authorization code that that can be used (e.g. by a back end service) to request a refresh token and thus gaining long lived access to resources.”

      But does’t the authorization code flow allow you to exchange an authorization code to request a refresh token?

      Does the hybrid flow help in any way with the scenario @RonyK describes, where using code flow, you’re exchanging the code into an access token in the back channel and then want to pass that token to the front-end? If so what would be the recommended way to pass it from back to front?

      Basically the scenario is this – you run aspnet mvc app, which hosts a js app that will call the web api. If you configure the aspnet mvc app with OIDC middleware, it will authenticate the first request and the client side libraries will load into the browser. From there, the client side code will need an access token to hit the web api. The issue is how to pass that access token initially and later on be able to refresh for a new access token without a full browser refresh. Does the hybrid flow provide anything to make this scenario any easier?

      • Alexey Auslender says:

        Since the SSO session is already established (using OIDC middleware as you mentioned before) you need to make a front channel roundtrip to the authorize endpoint again (using oidc.js) and grab the token from uri fragment.

      • Tom says:

        Thanks @Alexey Auslender (won’t let me reply directly to your comment)

        I haven’t used oidc.js before. Are you suggesting to use oidc.js to communicate with the authorization server after the middleware successfully authenticated the user on the server? What is the point of doing it server side then if I have repeat on the client side again?

      • Well – the “mixed mode” scenario with server and JS is not pretty – either way.

        * you can start the authN handshake on the server and render the token down to the client e.g. using a view
        * you can start the authN handshake on the server and use the hidden iframe approach on the client side to get a token
        * you can ditch the whole server side thing and more to JS

        you can always get new access tokens (without interrupting the user flow) on the client side as long as the authentication session is still valid.

      • Tom says:

        Thanks Dominick,
        Yes ‘mixed mode’ is the term I was looking for. So the hybrid flow doesn’t provide any means to make the ‘mixed mode’ scenario any easier, correct? I guess I was thrown off by its name, sounds like it was never the intent of it.

        Would you recommend using adaljs for the hidden iframe client side refresh? If I use oidc.js (just started looking into it), would that make the hidden iframe scenario any easier or eliminated the need for it altogether? Do you guys have any (identity server) samples out there of doing it?

      • ADAL is AAD only – if that’s what you use, go for it.

        otherwise have a look here
        https://github.com/IdentityModel/oidc-token-manager

  10. ppman5k says:

    Hi Dominick, I have a question about SSO with Identity Server. How I can enable the SSO from IdSvr with multi domains, I mean,We have two applications, both are on differents domains, and the IdSvr is hosted in an Azure API APP. As you know we can not share cookies from differents domains. Any suggestion will be appreciated.

    Thanks, have a great day!.

  11. Vishal Vyas says:

    Hi Dominick,

    Just wondering is there any easy way / out of box functionality available to achieve something like mentioned below for implementing security for a Mobile application which needs to call different Web APIs (assumption Mobile app and web apis are hosted by same enterprise)

    1) Token 1: Get this token for Mobile Device + Mobile Application combination from authentication server to established trust as a valid calling party (may be using something like client credential flow of OAuth)

    2) Token 2: Using above mentioned Token 1 and userid / password get token for user (id_token) for current Mobile Device + Mobile Application + User combination (may be using something like resource owner password flow of OAuth) and inside this id_token will have some reference to Token 1 so both tokens can only be used / validated together

    3) Token 3: Using above mentioned (Token 1 and Token 2 combination) get Token 3 which needs to be used to access Web api. This Token 3 will have reference to Token 1 and Token 2 so that Token 3 can alone cannot be replayed or used alone to get access to web api. (for example identity model sitting on web api side will check for existence of all three tokens and their validity together to allow access) rather than giving access just based on access token.

    The reason for asking this question is our security architect is suggesting following to two things which we are looking to achieve

    1) To keep Mobile application as dumb as possible (because anybody can reverse engineer a mobile application after downloading (specially deployed on Android platform) can then try to develop code which can act as that mobile application and hit web apis)

    2) Access tokens alone should not be sufficient enough on web api endpoint to grant access to user instead Access tokens always should be validated with the combination of calling party (Mobile Device + Mobile Application) and User token (id_token)

    I really appreciate your input on this.

    • To address the concerns –

      1) there is nothing you can do about that. If you hand out your code someone can reverse engineer it. If your APIs are public – anyone can call them. It is not worth looking into protection here – it is just the way it is.

      2) access tokens (at least in idsrv) already contain information about client and user information. Not sure how you would securely communicate device identification though.

      Check the idrsrv docs and samples – and feel free to ask more questions on the github issue tracker.

  12. Paul Mooney says:

    Hi Dominick,

    Upon receipt of a valid Access Token, is it considered best practice to invoke a call to the userinfo endpoint, and retrieve user metadata, for each subsequent call to your application, or should the call to userinfo instead be invoked once, and the user metadata response stored in, for example a cookie, such that subsequent requests read user metadata from the cookie as opposed to invoking a call to userinfo for each request.

  13. kkfrost says:

    Hello,
    Were using what’s in this sample to populate our AccessToken, expires_at and refresh_token. We try to do as much as we can in C# versus JavaScript. At this time, we’re trying to figure out the best way to implement a timer which initiate something in our backend code to refresh a users token before it expires?

    We have code to refresh a token and it works, we’re just trying to figure out the best way to trigger the call in an MVC application.

    Thanks

    • BMW22 says:

      I know this is an old question, but wanted to post in case others stumble upon this:

      I have solved this by using the OWIN middelware on the server and passing the access token to the client (via MVC HTML Helper extensions by hanging an object off the window object) and then having a JavaScript method (on load) that will take the token expiration and set a timer. It calls back into an MVC controller 30 seconds before the token expires – which kicks off the backend call to get a new access token. In the JS when the response is returned, I update the AccessToken property in my JS to the new one, and do the same logic where it checks the Expiration and sets the timer again.
      So when any API call is made from the JS, it just reaches into the object where I store the Access Token and gets it. This way it’s always the latest one.

      Seems to work well and gets me away from using both the oidc-client library along with the OWIN middleware which didn’t seem to work the way I wanted and had to get it’s own access token (so the server getting it was just a waste).

  14. Hi there,

    I am having no luck implementing the above code. n.ProtocolMessage.AccessToken is null.
    I could really use some help as I spent a ton of time figuring this out to no avail :(…

    The details can be found here:

    https://stackoverflow.com/questions/45443820/owin-hybrid-with-identityserver-3-authorization-code-is-too-long-error

Leave a comment