I am currently trying to create an identity server that only uses external provider (Google) for its authentication, I used React for the login UI that will pop-up once the user logs in and get redirected to my bff application.
PROBLEM:
There was no error, but after redirecting the web-client which is also made from react the application is unable to detect the authenticated user. I am unsure what is wrong if there was no error then what seems to be the issue?
Below is the React.BFF.UI that triggers the Google backend
I am using @axa-fr/react-oidc
for handling the openid in my webclient.
This will get triggered when the user clicks the google login button
const handleSubmitGoogle = async () => {
window.location.href = `/auth/google?returnUrl=http://localhost:5173/Login`;
}
builder configuration in my asp.net application
builder.Services.AddIdentityServer()
.AddInMemoryIdentityResources(IdentityConfig.IdentityResources)
.AddInMemoryApiScopes(IdentityConfig.ApiScopes)
.AddInMemoryClients(IdentityConfig.Clients)
.AddTestUsers(TestUsers.Users);
builder.Services.AddAuthentication()
.AddCookie("google-auth", options =>
{
options.Cookie.HttpOnly = true; // Makes the cookie inaccessible to client-side scripts
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Ensures the cookie is only sent over HTTPS
options.ExpireTimeSpan = TimeSpan.FromMinutes(60); // Sets the cookie expiration time
// Additional configuration...
})
.AddOpenIdConnect("Google", "Sign-in with Google", options =>
{
options.SignInScheme = "google-auth";
options.Authority = "https://accounts.google.com/";
options.ClientId = "";
options.ClientSecret = "";
options.ResponseType = OpenIdConnectResponseType.Code;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.CallbackPath = "/signin-google";
options.Scope.Add("openid"); // Required for an id_token
options.Scope.Add("profile"); // Optional, to include user profile information
options.Scope.Add("email"); // Already included for email claim
options.ResponseType = "code"; // Use "code" for Authorization Code flow
});
Lastly the callback method
public static async Task<IResult> GoogleLoginCallBack(HttpContext httpContext, IIdentityServerInteractionService interaction, IEventService events)
{
// Attempt to authenticate using the external cookie scheme
var result = await httpContext.AuthenticateAsync("google-auth");
if (!result.Succeeded)
{
// Log the error and handle the failure case appropriately
// Consider returning a user-friendly error message or redirecting to an error page
return Results.Problem("External authentication error.");
}
// Ensure we have an authenticated principal
if (result.Principal == null)
{
return Results.Problem("External authentication produced a null principal.");
}
var externalUser = result.Principal;
// Prepare for creating a local user session
var additionalLocalClaims = new List<Claim>();
var localSignInProps = new AuthenticationProperties();
CaptureExternalLoginContext(result, additionalLocalClaims, localSignInProps);
// Extract the user identifier from the external claims
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
externalUser.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim == null)
{
return Results.Problem("Unknown user identifier.");
}
// Extract the provider information
if (!result.Properties.Items.TryGetValue("scheme", out var scheme))
{
return Results.Problem("Provider scheme not found.");
}
var provider = scheme;
// Extract additional user information
var name = externalUser.FindFirst(ClaimTypes.GivenName)?.Value ?? "Unknown";
var subjId = userIdClaim.Value;
// Create a new IdentityServerUser
var issuer = new IdentityServerUser(subjId)
{
DisplayName = name,
IdentityProvider = provider,
AdditionalClaims = additionalLocalClaims,
};
// Sign in the user and clear the external cookie
await httpContext.SignInAsync(issuer, localSignInProps);
await httpContext.SignOutAsync("google-auth");
// Redirect the user to the return URL or a default route
var returnUrl = result.Properties.Items.TryGetValue("returnUrl", out var url) ? url : "~/";
var context = await interaction.GetAuthorizationContextAsync(returnUrl);
if (context != null)
{
}
return Results.Redirect(returnUrl);
}
I am greatly confuse on what is happening and I am losing hope, I am not sure what is wrong.
I tried the following approaches:
- read articles from medium regarding bff approach, please see one of them below
- Identity: Google Authentication with React and Asp.NET Identity
- watch youtube videos regarding react and bff implementation:
- Basics Part 2b: The “external authentication callback” Pattern
- Basics Part 2a: Adding external Authentication to your ASP.NET Core Application
- referred to github for projects related to bff and react:
- adding-a-react-18-user-interface-to-identityserver-6
EngrJames is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.