openiddict / openiddict-samples

.NET samples for OpenIddict

Home Page:https://documentation.openiddict.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

BFF pattern and keeping token in the backend

DennisvanZetten opened this issue · comments

Confirm you've already contributed to this project or that you sponsor it

  • I confirm I'm a sponsor or a contributor

Version

4.x

Question

Hi,

For a project we are working on we have React (SPA) clients, a proxy/gateway server and an Identity server with Openiddict. Everthing is working fine. One small issue though I can't seem to figure out.
Currently the token is packed into the cookie going to the client. I would like to keep the token in the backend, store it in a cacheprovider and not in the cookie. Is there a way to do this?

I tried the following:

 services.AddAuthentication(options =>
 {
     options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
     options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
     options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
 })
 .AddCookie(options =>
 {
     options.Cookie.Name = AuthCookieName.CookieName;
     options.ExpireTimeSpan = TimeSpan.FromHours(8);
     options.SlidingExpiration = false;
     options.Cookie.HttpOnly = true;
     options.Cookie.Domain = cookieDomain;
     options.Cookie.IsEssential = true;
     options.Cookie.SameSite = SameSiteMode.Strict;
     options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
     options.AccessDeniedPath = "/forbidden";
     options.LoginPath = "/login";
     options.LogoutPath = "/logout";
 })
 .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
 {
     options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
     options.Authority = openIdConnectSettings["Authority"];
     options.ClientId = openIdConnectSettings["ClientId"];
     options.ClientSecret = openIdConnectSettings["ClientSecret"];
     options.RequireHttpsMetadata = false;
     options.ResponseType = OpenIdConnectResponseType.Code;
     options.ResponseMode = "form_post";
     options.UsePkce = true;
     options.CallbackPath = "/signin-oidc";
     options.SignedOutCallbackPath = "/signout-callback-oidc";
     options.Scope.Clear();
     options.Scope.Add("profile");
     options.Scope.Add("roles");
     options.Scope.Add("openid");
     options.SaveTokens = false;
     options.Events.OnRedirectToIdentityProvider = context =>
     {
         var consumerApp = context.Request.Query["consumer_app"].ToString();
         if (!string.IsNullOrEmpty(consumerApp))
         {
             context.ProtocolMessage.RedirectUri += $"?consumer_app={consumerApp}";
         }
         return Task.CompletedTask;
     };
     options.Events.OnRedirectToIdentityProviderForSignOut = context =>
     {
         context.ProtocolMessage.ClientId = openIdConnectSettings["ClientId"];
         
         var consumerApp = context.Request.Query["consumer_app"].ToString();
         if (!string.IsNullOrEmpty(consumerApp))
         {
             context.ProtocolMessage.PostLogoutRedirectUri += $"?consumer_app={consumerApp}";
             context.ProtocolMessage.RedirectUri += $"?consumer_app={consumerApp}";
         }
         return Task.CompletedTask;
     };
     options.Events.OnTicketReceived = async context =>
     {
         var cache = context.HttpContext.RequestServices.GetRequiredService<ICacheProvider>();


         var identity = context!.Principal!.Identities.FirstOrDefault();

         if (identity == null || !identity.IsAuthenticated)
         {
             context.Fail("Failed authentiation");
             return;
         }


         var user = context.HttpContext.User;
         var userId = identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
         var token = await context.HttpContext.GetTokenAsync("access_token");
         await cache.SetAsync(userId, token, TimeSpan.FromHours(8));
         return;
     };
 });

This doesn't seem to work though.
Other ideas?

Regards,
Dennis

Hey,

This doesn't seem to work though.

I suspect this is caused by two things:

  • You set SaveTokens to false (which the default value). For the tokens to be added to the AuthenticationProperties instance, you'll need to set SaveTokens to true so they are accessible from OnTicketReceived. If you don't want them to be persisted in the generated authentication cookie, you can remove them from AuthenticationProperties in your OnTicketReceived delegate.

  • You're likely not resolving the access_token token from the right place, as GetTokenAsync("access_token") uses the default authentication scheme configured in the authentication options (here, CookieAuthenticationDefaults.AuthenticationScheme) instead of using the OIDC scheme. Consider resolving the access token from the context.Properties bag instead.

Note: Microsoft's OIDC handler should still work fine, but the OpenIddict client is now the recommended option for all scenarios. All the samples in this repository now use it if you're interested in giving it a try (e.g https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Startup.cs)

Hi,

Thanks for the hint.
I will try to implement it like this.

@DennisvanZetten hey. Did that work for you or do you still need help? 😃

Closing, but feel free to reopen if you still need help ❤️