DEV Community

Cover image for Setting up an Authorization Server with OpenIddict - Part VI - Refresh tokens
Robin van der Knaap
Robin van der Knaap

Posted on

Setting up an Authorization Server with OpenIddict - Part VI - Refresh tokens

This article is part of a series called Setting up an Authorization Server with OpenIddict. The articles in this series will guide you through the process of setting up an OAuth2 + OpenID Connect authorization server on the the ASPNET Core platform using OpenIddict.

GitHub logo robinvanderknaap / authorization-server-openiddict

Authorization Server implemented with OpenIddict.


Access tokens typically have a short lifetime for security reasons. In this part we will enable the usage of refresh tokens. A refresh token allows an application to obtain a new access token without prompting the user.

Enable refresh tokens

First we need to enable the refresh token flow in Startup.cs:

options
    .AllowAuthorizationCodeFlow()
        .RequireProofKeyForCodeExchange()
    .AllowClientCredentialsFlow()
    .AllowRefreshTokenFlow();
Enter fullscreen mode Exit fullscreen mode

In the token endpoint in the AuthorizationController we need to handle refresh token requests, just like we handled client credentials requests, and requests to exchange authorization codes for access tokens:

[HttpPost("~/connect/token")]
public async Task<IActionResult> Exchange()
{
    ...

    else if (request.IsAuthorizationCodeGrantType())
    {
        ...
    }

    else if (request.IsRefreshTokenGrantType())
    {
        // Retrieve the claims principal stored in the refresh token.
        claimsPrincipal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

Basically, we only need to retrieve the ClaimsPrincipal from the refresh token, and sign in again, which will return a new access token.

Finally, we need to allow the client to use refresh tokens:

Permissions =
{
    OpenIddictConstants.Permissions.Endpoints.Authorization,
    OpenIddictConstants.Permissions.Endpoints.Token,

    OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
    OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,
    OpenIddictConstants.Permissions.GrantTypes.RefreshToken,

    OpenIddictConstants.Permissions.Prefixes.Scope + "api",

    OpenIddictConstants.Permissions.ResponseTypes.Code
}
Enter fullscreen mode Exit fullscreen mode

If a client wants to use refresh tokens, it needs to request the offline_access scope. Just like we requested the openid scope for identity tokens. Now, if we request a new access token with Postman, we will receive a refresh token together with the access token.

After that, we can use the refresh token to request a new access token with Postman. Send a POST request to /connect/token with a body containing the client credentials, grant type and of course the refresh token.

Alt Text

In the response you will find a new access token and also a new refresh token. So, refresh tokens are automatically rotated, which is more secure than reusing the same refresh token.

Top comments (18)

Collapse
 
rmaurodev profile image
Ricardo

You can use options.EnableLogoutEndpointPassthrough();

Collapse
 
uic19bca1130 profile image
Ankush

{
"error": "invalid_grant",
"error_description": "The specified token is not a refresh token.",
"error_uri": "documentation.openiddict.com/error..."
}
give me solution of this error

Collapse
 
luisapplivity profile image
LuisApplivity

Hi Ankush,

The solution in your case is most likely the same one that was required by most here who had the same issue: When you copy+paste the Refresh Token (or any token) from the Postman user interface, it also adds an extra paragraph character in the end of your token. So when you paste your token, press the Backspace key once to delete it.

If that does not work (unlikely), then your token is either the wrong one like Oleg suggested, or it even expired, or what was pasted/written is simply something else entirely.

Collapse
 
olegtar profile image
Oleg

I suppose you send an access token instead of a refresh token
you need enable "offline_access" scope to get refresh token

Collapse
 
rezapouya profile image
Reza Pouya

this error is thrown for me

{
"error": "invalid_grant",
"error_description": "The specified token is not a refresh token.",
"error_uri": "https://documentation.openiddict.com/errors/ID2007"
}

what should i do to take refresh-token?

Collapse
 
rezapouya profile image
Reza Pouya

i mean , i just sigin in with client-id and client-secret and just take one token. this token should be acess token , then how i get refresh token ?

Collapse
 
rezapouya profile image
Reza Pouya

i found it , i just should add this to my access token request .

scope => api offline_access

Collapse
 
vijay_badugu_f4240e6c60fb profile image
Vijay Badugu

Please provide right grant_type

Collapse
 
madameczek profile image
Marek • Edited

Hi, Thanks for simple yes complete and inspiring example.
I've implemented it with Microsoft Identity with ef as persistence. Works fine until I request exchange of refresh token for a new at.
So, I ran author's example from github as reference and got the same result which is:

info: OpenIddict.Server.OpenIddictServerDispatcher[0]
      The request address matched a server endpoint: Token.
info: OpenIddict.Server.OpenIddictServerDispatcher[0]
      The token request was successfully extracted: {
        "refresh_token": "[redacted]",
        "grant_type": "refresh_token",
        "client_id": "postman",
        "client_secret": "[redacted]"
      }.
info: OpenIddict.Server.OpenIddictServerDispatcher[0]
      The response was successfully returned as a JSON document: {
        "error": "invalid_grant",
        "error_description": "The specified token is invalid.",
        "error_uri": "https://documentation.openiddict.com/errors/ID2004"
      }.
Enter fullscreen mode Exit fullscreen mode

I checked everything. App registration (permissions) is fine. Grant looks correct. Currently I use postman, but experience the same when using a front-end library (can't say the name right now, but it is used with a React app).

Log shows, that the request was recognised, but then refresh token wasn't recognised as valid by openiddict.
When put to jwt.io, it says an error: "JWT payload is not a valid JSON object. JWT payload must be top level JSON object..."
Actually I can't influent JWT refresh token as it is prepared by the library. Maybe it needs additional configuration?
Any idea what can be done here to fix the problem?

Collapse
 
abdelrahmankamall profile image
Abdelrahman-kamall

same here , have u found the solution ?
one more thing i noticed when i try to refresh the token from the built in refresh in postman

Image description
i think it works

Image description

and if i used breakpoints and debugged it actually enters the action of the connect/token end point

although if i used inspect the request after this refresh produces this

Image description

meanwhile the post request for the refresh token doesn't enter the action while useing the debugger , but produces this in post man

Image description

i guess the request itself has something missing ?

Collapse
 
abdelrahmankamall profile image
Abdelrahman-kamall

it turned out to be that when i copy the refresh token from the stored token in postman to put it in my post methed it pastes it with an extra new line resulted from an extra /n or /r maybe ,, so after removing it it worked fine ,
also don't forget to update the used tokens (access and refresh) after calling the post method each time

Thread Thread
 
luisapplivity profile image
LuisApplivity

Yep! I'm sure this caught a lot of people.

Collapse
 
capesean profile image
Sean Walsh

Thanks Robin. I needed to add the following lines, to include the refresh token in the jwt, not sure if it's necessary in your codebase though:

github.com/openiddict/openiddict-s...

Collapse
 
capesean profile image
Sean Walsh

Ah, never mind. I see you have it there:
github.com/robinvanderknaap/author...

Collapse
 
timothydowd profile image
Tim Dowd

embassador, with these rocher your spoil us

Collapse
 
vijay_badugu_f4240e6c60fb profile image
Vijay Badugu

I have a question regarding calling Authorization Server from a different .NET Framework web forms application or Asp.net Core client application, I tried what you have suggested in the blog, I am able to generate authorization code and iss attached to query string. Do i need to send post request to token end point to get the access token or will it automatically execute token end point? I have no clue how it works from a client web applications. I tried to use some OpenID Microsoft Namespaces to connect to the Authorization Server and I am receiving an error. Could you please guide me to proceed further on this.

InvalidOperationException: Cannot redirect to the authorization endpoint, the configuration may be missing or invalid.

.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority

Collapse
 
hypervtechnics profile image
hypervtechnics

Great series! Thanks!

Would you make another article on how to leverage custom grants?

Collapse
 
thiagofferreira profile image
Thiago Ferreira

How to implement the logout flow, where I need to return a redirect_uri to the client, and also invalidate the id_token?