Confused on how to get access tokens from B2C in Blazor App Confused on how to get access tokens from B2C in Blazor App azure azure

Confused on how to get access tokens from B2C in Blazor App


I have created a sample application with OpenId Connect and Blazor (Server) for you here https://github.com/yberstad/BlazorAuth. It is uses a SameSiteCookie and OpenId Connect.

  1. In the appsettings.json, fill in your Authority, ClientId and ClientSecret.
  2. Configure the redirect URi in Azure to http://localhost:62438/signin-oidc/
  3. Start up the app in debug mode
  4. Got to http://localhost:62438/api/openidconnect/login
  5. Login with Azure
  6. Got to http://localhost:62438/api/openidconnect/user, having a break point in the OpenIdConnectController.GetUser action, there you can see how to getting hold of the access token.

How to get access and refresh token:

var accessToken = await HttpContext.GetTokenAsync("access_token");var refreshToken = await HttpContext.GetTokenAsync("refresh_token");

SameSiteCookie Info:https://brockallen.com/2019/01/11/same-site-cookies-asp-net-core-and-external-authentication-providers/

Getting Access Token:http://docs.identityserver.io/en/latest/quickstarts/5_hybrid_and_api_access.html#using-the-access-token

Having the tokens stored in a SameSiteCookie makes it only visible for the server, hence not saving and exposing it in an unsafe environment on the client. A SameSiteCookie is also safe for XSS.

Hope this helps.


I was able to solve this myself. My AcquireTokenSilent call was failling because there was no users in the cache when I call it, so I had to make sure to add first entry to the cache when my user logs in. I was able to achieve this by configuring my auth as follows:

services.AddAuthentication(sharedOptions =>            {                sharedOptions.DefaultScheme = AzureADB2CDefaults.AuthenticationScheme;                sharedOptions.DefaultChallengeScheme = AzureADB2CDefaults.OpenIdScheme;            })               .AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options))               .AddCookie();            services.Configure<OpenIdConnectOptions>(AzureADB2CDefaults.OpenIdScheme, options =>            {                //Configuration.Bind("AzureAdB2C", options);                options.ResponseType = Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectResponseType.CodeIdToken;                options.Scope.Add("offline_access");                options.Scope.Add("https://mytenant.onmicrosoft.com/api/api.read.write");                options.SaveTokens = true;                options.GetClaimsFromUserInfoEndpoint = true;                options.Events.OnAuthorizationCodeReceived = async context =>            {                AzureADB2COptions opt = new AzureADB2COptions();                Configuration.Bind("AzureAdB2C", opt);                // As AcquireTokenByAuthorizationCodeAsync is asynchronous we want to tell ASP.NET core that we are handing the code                // even if it's not done yet, so that it does not concurrently call the Token endpoint. (otherwise there will be a                // race condition ending-up in an error from Azure AD telling "code already redeemed")                context.HandleCodeRedemption();                var code = context.ProtocolMessage.Code;                string signedInUserID = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;                IConfidentialClientApplication cca = ConfidentialClientApplicationBuilder.Create(opt.ClientId)                .WithB2CAuthority(opt.Authority)                .WithRedirectUri(opt.RedirectUri)                .WithClientSecret(opt.ClientSecret)                .WithClientName("myWebapp")                .WithClientVersion("0.0.0.1")                .Build();                new MSALStaticCache(signedInUserID, context.HttpContext).EnablePersistence(cca.UserTokenCache);                try                {                    AuthenticationResult result = await cca.AcquireTokenByAuthorizationCode(opt.ApiScopes.Split(' '), code)                        .ExecuteAsync();                    context.HandleCodeRedemption(result.AccessToken, result.IdToken);                }                catch (Exception)                {                }            };            });