0

I'm currently struggling with access token lifetime. I have dotnet core Web Application and dotnet core Web API.

The web application is protected with OpenIDConnect authorization. Once you try to connect into web app, you are redirected to Microsoft login form and after successful login, the Access Token is provided and stored into cookie together with Refresh Token.

Therefore, the Access Token is passed in Authorization Header for my WebAPI request. When the access_token lifetime expires, then my WebAPI starts to return 401 Unauthorized.

I read a lot articles about revoking access token by using refresh token, but I didn't find any implementation example, so I turn to you guys.

This is how I am setting up the OpenId in Web Client.

        services.AddDataProtection();
        services.AddAuthorization();
        services.AddWebEncoders();
        services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
            options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.ClientId = Configuration["AzureAd:ClientId"];
            options.Authority = $"{Configuration["AzureAd:AadInstance"]}{Configuration["AzureAd:Tenant"]}/v2.0";
            options.ClientSecret = Configuration["AzureAd:ClientSecret"];
            options.ResponseType = "code";
            options.SaveTokens = true;
            options.UseTokenLifetime = true;
            
            options.Scope.Add(Configuration["AzureAd:Scope"]);

            options.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuer = Configuration["AzureAd:Tenant"] != "common",
                RoleClaimType = JwtClaimTypes.Role
            };
            options.Events = new OpenIdConnectEvents
            {
                OnRemoteFailure = context =>
                {
                    context.HandleResponse();
                    context.Response.Redirect("/error");
                    return Task.CompletedTask;
                }
            };
        });

        services.AddHttpContextAccessor();

This is how I am setting up authentication in Web API Startup.cs.

            services.AddAuthentication("Bearer")
            .AddJwtBearer(
                "Bearer",
                options =>
                {
                    options.Authority = $"{Configuration["AzureAd:AadInstance"]}{Configuration["AzureAd:Tenant"]}/v2.0";
                    options.Audience = Configuration["AzureAd:Audience"];
                    options.TokenValidationParameters.ValidateIssuer = false;
                });

And lastly, this is constructor of my ApiService, where I am adding access token to headers.

    protected ApiService(HttpClient httpClient, string apiUri, IHttpContextAccessor httpContextAccessor, ILogger<ApiService> logger)
    {
        this.httpClient = httpClient;
        this.apiUri = apiUri;
        this.logger = logger;
        context = httpContextAccessor.HttpContext;

        this.httpClient.DefaultRequestHeaders.Authorization
            = new AuthenticationHeaderValue("Bearer", context.GetTokenAsync("access_token").Result);
    }

If you need guys any more information, tell me and I will provided it. Thank you!

Hong Ooi
  • 56,353
  • 13
  • 134
  • 187
Daniel Rusnok
  • 449
  • 1
  • 7
  • 14
  • Just to understand correctly: first - you call ASP .NET Core Web API from the ASP .NET Core Web App. Second question - you would like to revoke refresh token correct for a specific user correct? – Daniel Krzyczkowski Aug 24 '20 at 09:49
  • 1) Correct 2) I would like to revoke access token by using refresh token (Authorization Code Flow princip) But after a few more searches it seems like this is a built-in function. So I am not sure if it is a solution for my issue. – Daniel Rusnok Aug 24 '20 at 10:39
  • @DanielRusnok I've edited the title to reflect your comments, feel free to change it back if this is incorrect – Hong Ooi Aug 24 '20 at 13:49
  • 1
    @DanielRusnok Exactly but from our discussion now I think that the answer to your question is here: https://stackoverflow.com/questions/48952087/get-refresh-token-with-azure-ad-v2-0-msal-and-asp-net-core-2-0 You can use MSAL to refresh token. Check this one also - here is the explanation: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/1008 – Daniel Krzyczkowski Aug 25 '20 at 09:52

2 Answers2

1

As I undarstand now - you have basic ASP .NET Core Web application (MVC, or Razor) and you want to secure it with Azure AD.

If my understanding is correct, you should leverage Microsoft.Identity.Web library: https://github.com/AzureAD/microsoft-identity-web

It is currently still in preview but I can confirm that it works stable. Here is detailed instruction how to integrate it with ASP .NET Core web app: https://github.com/AzureAD/microsoft-identity-web/wiki/web-apps

Here are the samples: https://github.com/AzureAD/microsoft-identity-web/wiki/web-app-samples

This library also manages refreshing token and provides token cache implementation so you do not have to implement it on your own: https://github.com/AzureAD/microsoft-identity-web/issues/221

Daniel Krzyczkowski
  • 2,732
  • 2
  • 20
  • 30
  • Thank you. But not quite what I meant. – Daniel Rusnok Aug 24 '20 at 11:05
  • OK, sad to hear that. I thought that we are talking about refresh token revocation in the Azure AD. – Daniel Krzyczkowski Aug 24 '20 at 11:08
  • I will try to explain it more. Sorry, it is my English, which is mostly sabotage my communication skills. I have Web Api and Web Client App(Dotvvm framework). My problem is that sometimes the Web Api is returning 401 Access Denied after a while of using Client App. I think it is a because of access token expiration. – Daniel Rusnok Aug 24 '20 at 11:11
  • 1
    The only solution now is to sign out and sign in into application and get new access token. I would like to get new access token silently "behind the scene" by using refresh token, but I don't know how, because I can't find any code examples. – Daniel Rusnok Aug 24 '20 at 11:13
  • I think that this behavior is built-in and I guess that my settings are wrong. Or, should I use ConfidentialClientApplication class to access the access token somehow and renew it? – Daniel Rusnok Aug 24 '20 at 11:16
  • So ITokenAcquisition.GetAccessTokenForUserAsync is exactly what I implement in my IBearerTokenHandler? Does this method handles issue when access token's lifetime expires? – Daniel Rusnok Aug 25 '20 at 04:53
0

Reprogramming the authentication in Startups and take advantage of ITokenAcquirer service solve my problem: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/4-WebApp-your-API/4-1-MyOrg

Daniel Rusnok
  • 449
  • 1
  • 7
  • 14