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
tofalse
(which the default value). For the tokens to be added to theAuthenticationProperties
instance, you'll need to setSaveTokens
totrue
so they are accessible fromOnTicketReceived
. If you don't want them to be persisted in the generated authentication cookie, you can remove them fromAuthenticationProperties
in yourOnTicketReceived
delegate. -
You're likely not resolving the
access_token
token from the right place, asGetTokenAsync("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 thecontext.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 ❤️