As a result of our meeting, I am sharing the code blocks you requested below:
OpenIdConnect Configuration:
context.Services.AddAuthentication().AddOpenIdConnect();
context.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = configuration["AzureAd:Instance"];
options.ClientId = configuration["AzureAd:ClientId"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.CallbackPath = configuration["AzureAd:CallbackPath"];
options.ClientSecret = configuration["AzureAd:ClientSecret"];
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SignInScheme = IdentityConstants.ExternalScheme;
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = redirectContext =>
{
redirectContext.ProtocolMessage.Prompt = "login";
return Task.CompletedTask;
}
};
options.Scope.Add("email");
options.Scope.Add("openid");
options.Scope.Add("offline_access");
options.Scope.Add("profile");
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
});
Custom register model to create user automatically:
[ExposeServices(typeof(RegisterModel))]
public class MyRegisterModel : RegisterModel
{
public MyRegisterModel(IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions, IAccountExternalProviderAppService accountExternalProviderAppService, ICurrentPrincipalAccessor currentPrincipalAccessor, IHttpClientFactory httpClientFactory) : base(schemeProvider, accountOptions, accountExternalProviderAppService, currentPrincipalAccessor, httpClientFactory)
{
}
public override async Task<IActionResult> OnGetAsync()
{
ExternalProviders = await GetExternalProviders();
if (!await CheckSelfRegistrationAsync())
{
if (IsExternalLoginOnly)
{
return await OnPostExternalLogin(ExternalLoginScheme);
}
Alerts.Warning(L["SelfRegistrationDisabledMessage"]);
return Page();
}
if (IsExternalLogin)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
{
Logger.LogWarning("External login info is not available");
return RedirectToPage("./Login");
}
var identity = externalLoginInfo.Principal.Identities.First();
var emailClaim = identity.FindFirst(AbpClaimTypes.Email) ?? identity.FindFirst(ClaimTypes.Email);
if (emailClaim == null)
{
throw new AbpException("Could not find an email address for the user from the external login info!");
}
var userName = await UserManager.GetUserNameFromEmailAsync(emailClaim.Value);
var user = await RegisterExternalUserAsync(externalLoginInfo,userName, emailClaim.Value);
await SignInManager.SignInAsync(user, isPersistent: true, authenticationMethod: ExternalLoginScheme);
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return Redirect(ReturnUrl ?? "/");
}
Alerts.Warning(L["SelfRegistrationDisabledMessage"]);
return Page();
}
}
Disable local login in appsettings.json:
"Settings": {
"Abp.Account.EnableLocalLogin":false
}
Note: After adding this setting, if it does not appear as false when you start the application, it is because the value stored in the database overrides the one in the appsettings. If you create a new database from scratch, the value in the appsettings will be used since there will be no conflicting setting in the database.
If you have no further questions on the subject, can I close the ticket?
See you soon š
Hi,
No, it doesnāt mean that you have to maintain a separate Auth Server. In a modular monolith setup, your final application, which hosts all the modules, does not need to be tiered.
If you havenāt already completed it, I recommend going through this tutorial to better understand how to build a modular monolith application and the benefits of this architecture.
To sum up, using a modular monolith architecture does not require you to host a separate Auth Server or maintain an Auth Server. If you'd like, I can show you a quick demo during our meeting on Thursday to clarify this further.
Hi š,
From your Gif it seems that in addition to the Account module you also installed the OpenIddict UI module. Creating a non-tiered solution means using OpenIddict included in ABP, right?
Yes, that's correct. When you create a non-tiered solution, OpenIddict is included via ABP modules. However, since itās encapsulated within a module, you donāt need to manage or deploy it separately. The inclusion of the OpenIddict UI module should not be seen as a drawbackāit simply gives you flexibility if you ever decide to customize authentication flows in the future.
As for Jesseās point:
"Just to clarify. I want to use an external OIDC ID provider as the main login to my app. I do not want to use or maintain Auth server. Is this possible with ABP?"
Yes, this is absolutely possible. I believe what Jesse meant is that he does not want to maintain a separate AuthServer project. In a non-tiered solution, everything is hosted in a single application, and ABP takes care of the underlying OpenIddict setup through module references. So you're not responsible for deploying or maintaining a separate authorization serverāitās just a package dependency managed by the ABP team.
If I were to have multiple instances of my backend application this scenario would not work.
ABP is designed to support scalable, multi-instance deployments. Thereās no technical limitation preventing you from running multiple instances of your backend in this scenario. If you face any specific issues with this, I recommend opening a separate issue so we can help investigate further.
Thanks for bringing up this topicādefinitely valuable for many of us working with ABP.
Hi Jesse š,
Yes, it's possible to use an external OIDC identity provider as the main login without using the ABP AuthServer.
To do this:
"Allow to register and log in with local username and password" setting, as mentioned in the answer, so ABP doesnāt redirect to the AuthServer anymore.In the GIF below, I demonstrated this with Azure Entra ID, but you can apply the same approach with Auth0 ā there's no limitation.
ā ļø Important: Once you disable local login, you wonāt be able to sign in with the default admin user anymore. So before logging out, assign the admin role to the user who logs in through Auth0. Otherwise, youāll lose access to full permissions unless you manually update the database.
Let me know if you need help with the configuration.
Hello Jesse š,
I hope your trial license is going well āŗļø
To resolve this, please disable the setting highlighted in the screenshot below. Once disabled, the OpenID Connect provider you have configured will be used automatically.
Best regards,
Berkan ÅaÅmaz
Developer Advocate
https://www.berkansasmaz.com
Hi Ademaygun,
Thank you for your recent valuable feedback. I will open a separate issue to track this(#20294). We will proceed with using the suggested logos for Google and other popular options.
Closing this issue now. Feel free to re-open it or create a new one if you have further questions.
Hello Waqar,
Sorry for the late reply. I'm currently working on your issue. While trying to reproduce the problem, I discovered a different bug, which I'm addressing at the moment. Once that's resolved, Iāll continue working on reproducing your issue. If I'm still unable to reproduce it, I may reach out to request some additional information. Thank you for your understanding.
Hello, I used the information you provided to reproduce the problem. As a result, I added a middleware to the Blazor application as shown below. As you can see, I am able to access the access token, and the current user information is populated correctly. Could you clarify exactly where you are experiencing the issue?
OpenIdConnectOptions: