Olti
01/21/2025, 12:18 PMOlti
01/21/2025, 12:19 PMusing Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Web.Common.Security;
namespace HunzikerIntranet.HQM.Umbraco.Security;
public class EntraIDB2CMembersExternalLoginProviderOptions : IConfigureNamedOptions<MemberExternalLoginProviderOptions>
{
public const string SchemeName = "ActiveDirectoryB2C";
public void Configure(string? name, MemberExternalLoginProviderOptions options)
{
if (name != Constants.Security.MemberExternalAuthenticationTypePrefix + SchemeName)
{
return;
}
Configure(options);
}
public void Configure(MemberExternalLoginProviderOptions options)
{
options.AutoLinkOptions = new MemberExternalSignInAutoLinkOptions(
autoLinkExternalAccount: true,
defaultCulture: null,
defaultIsApproved: true,
defaultMemberTypeAlias: Constants.Security.DefaultMemberTypeAlias
)
{
OnAutoLinking = (autoLinkUser, loginInfo) =>
{
// Customize Member creation if necessary.
},
OnExternalLogin = (user, loginInfo) =>
{
return true;
}
};
}
}
Olti
01/21/2025, 12:19 PMnamespace HunzikerIntranet.HQM.Umbraco.Security;
public static class MemberAuthenticationExtensions
{
public static IUmbracoBuilder ConfigureAuthenticationMembers(this IUmbracoBuilder builder)
{
var config = builder.Services.BuildServiceProvider().GetRequiredService<IConfiguration>();
var azureAdOptions = config.GetSection("AzureAd").Get<AzureAdOptions>();
builder.Services.ConfigureOptions<EntraIDB2CMembersExternalLoginProviderOptions>();
builder.AddMemberExternalLogins(logins =>
{
logins.AddMemberLogin(membersAuthenticationBuilder =>
{
membersAuthenticationBuilder.AddMicrosoftAccount(
membersAuthenticationBuilder.SchemeForMembers(
EntraIDB2CMembersExternalLoginProviderOptions.SchemeName
),
options =>
{
options.ClientId = azureAdOptions.ClientId;
options.ClientSecret = azureAdOptions.ClientSecret;
options.CallbackPath = azureAdOptions.CallbackPath;
options.TokenEndpoint = $"{azureAdOptions.Instance}{azureAdOptions.TenantId}/oauth2/v2.0/token";
options.AuthorizationEndpoint = $"{azureAdOptions.Instance}{azureAdOptions.TenantId}/oauth2/v2.0/authorize";
options.SaveTokens = true;
}
);
});
});
return builder;
}
}
public class AzureAdOptions
{
public string Instance { get; set; }
public string TenantId { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string CallbackPath { get; set; }
}
Olti
01/21/2025, 12:19 PMbuilder.CreateUmbracoBuilder()
.AddBackOffice()
.AddWebsite()
.AddDeliveryApi()
.AddComposers()
.ConfigureAuthenticationMembers() // Added this
.Build();
Olti
01/21/2025, 12:20 PM"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR-TENANT-ID",
"ClientId": "YOUR-CLIENT-ID",
"ClientSecret": "YOUR-CLIENT-SECRET",
"CallbackPath": "/umbraco-b2c-members-signin"
}
Olti
01/21/2025, 12:22 PMhttps://localhost:44367/umbraco-b2c-members-signin
The Problem:
When I get redirected to the callback URL (/umbraco-b2c-members-signin), I see the following error:
The oauth state was missing or invalid.
The full exception trace mentions:
Microsoft.AspNetCore.Authentication.AuthenticationFailureException: The oauth state was missing or invalid.
Suspicions:
I suspect the issue might be related to:
- The App Registration in Azure: Perhaps I’ve missed something while setting it up.
- The OAuth State Parameter: Something seems wrong with how the state is being generated or maintained.
My Goal:
- To allow members from my company (identified by our domain name, e.g., @mycompany.com) to log in using Microsoft Entra ID.
- Once logged in, the member should be auto-created in Umbraco (if not already present) and granted access to protected member areas.Olti
01/21/2025, 12:24 PMkdx-perbol
01/21/2025, 2:07 PMkdx-perbol
01/21/2025, 2:08 PMkdx-perbol
01/21/2025, 2:09 PMkdx-perbol
01/21/2025, 2:12 PMTokenEndpoint
and AuthorizationEndpoint
correct? appSettings => interpolated string etc.Daniel Andersson
01/21/2025, 2:24 PMkdx-perbol
01/21/2025, 2:34 PMkdx-perbol
01/21/2025, 2:37 PMumbraco-b2c-members-signin
looks like it's handled internally by Umbraco? Or does Umbraco register this callback path route and connect it to these Member Auth Options?Olti
01/21/2025, 2:37 PMOlti
01/21/2025, 2:38 PMkdx-perbol
01/21/2025, 2:39 PMOlti
01/21/2025, 2:39 PMOlti
01/21/2025, 2:42 PMkdx-perbol
01/21/2025, 2:45 PMhttps://localhost:44367/umbraco-b2c-members-signin
? To match the callback URL in the member auth options.Olti
01/21/2025, 2:45 PMOlti
01/21/2025, 2:45 PMOlti
01/21/2025, 2:46 PMkdx-perbol
01/21/2025, 2:46 PMkdx-perbol
01/21/2025, 2:46 PMOlti
01/21/2025, 2:46 PMOlti
01/21/2025, 2:47 PMkdx-perbol
01/21/2025, 2:48 PMOlti
01/21/2025, 2:50 PMOlti
01/21/2025, 2:50 PMkdx-perbol
01/21/2025, 2:50 PMOlti
01/21/2025, 2:50 PMOlti
01/21/2025, 2:52 PMOlti
01/21/2025, 2:54 PMOlti
01/21/2025, 2:54 PMOlti
01/21/2025, 2:54 PMkdx-perbol
01/21/2025, 2:55 PMkdx-perbol
01/21/2025, 2:55 PMkdx-perbol
01/21/2025, 2:57 PMOlti
01/21/2025, 2:59 PMOlti
01/21/2025, 2:59 PMOlti
01/21/2025, 3:08 PMOlti
01/21/2025, 3:11 PMOlti
01/21/2025, 3:11 PMOlti
01/21/2025, 3:13 PMnamespace MyApp;
public static class MemberAuthenticationExtensions
{
public static IUmbracoBuilder ConfigureAuthenticationMembers(this IUmbracoBuilder builder)
{
builder.Services.ConfigureOptions<EntraIDB2CMembersExternalLoginProviderOptions>();
builder.AddMemberExternalLogins(logins =>
{
builder.Services.ConfigureOptions<EntraIDB2CMembersExternalLoginProviderOptions>();
builder.AddMemberExternalLogins(logins =>
{
logins.AddMemberLogin(
membersAuthenticationBuilder =>
{
membersAuthenticationBuilder.AddMicrosoftAccount(
membersAuthenticationBuilder.SchemeForMembers(EntraIDB2CMembersExternalLoginProviderOptions.SchemeName),
options =>
{
options.CallbackPath = "/umbraco-aad-members-signin";
options.ClientId = "5censoredd3f";
options.ClientSecret = "ZycensoredGG";
options.TokenEndpoint = $"https://login.microsoftonline.com/53acensored05c9/oauth2/v2.0/token";
options.AuthorizationEndpoint = $"https://login.microsoftonline.com/53a28censored05c9/oauth2/v2.0/authorize";
options.SaveTokens = true;
});
});
});
});
return builder;
}
}
Olti
01/21/2025, 3:14 PMusing Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Web.Common.Security;
namespace MyApp;
public class EntraIDB2CMembersExternalLoginProviderOptions : IConfigureNamedOptions<MemberExternalLoginProviderOptions>
{
public const string SchemeName = "ActiveDirectory";
public void Configure(string? name, MemberExternalLoginProviderOptions options)
{
if (name != Constants.Security.MemberExternalAuthenticationTypePrefix + SchemeName)
{
return;
}
Configure(options);
}
public void Configure(MemberExternalLoginProviderOptions options)
{
options.AutoLinkOptions = new MemberExternalSignInAutoLinkOptions(
autoLinkExternalAccount: true,
defaultCulture: null,
defaultIsApproved: true,
defaultMemberTypeAlias: Constants.Security.DefaultMemberTypeAlias
)
{
OnAutoLinking = (autoLinkUser, loginInfo) =>
{
},
OnExternalLogin = (user, loginInfo) =>
{
return true;
}
};
}
}
Olti
01/21/2025, 3:49 PMkdx-perbol
01/21/2025, 7:52 PMkdx-perbol
01/21/2025, 7:53 PMOlti
01/22/2025, 8:50 AMOlti
01/22/2025, 8:51 AMOlti
01/22/2025, 8:53 AMOlti
01/22/2025, 8:54 AMOlti
01/22/2025, 9:04 AMkdx-perbol
01/22/2025, 9:12 AMOlti
01/22/2025, 9:12 AMOlti
01/22/2025, 9:12 AMOlti
01/22/2025, 9:12 AMOlti
01/22/2025, 9:17 AM@inherits UmbracoViewPage<SiteBuilderBlock>
@using USNSiteBuilder.Core.Models
@using USNSiteBuilder.Core.Interfaces
@inject ISiteBuilderService _siteBuilderService
@{
//Available as Component Block, Split Component Block and Pod Block types
var content = (MicrosoftLogin)Model.BlockContent;
var settings = (MicrosoftLoginBlockSettings)Model.BlockSettings;
var externalLoginUrl = Url.Content("~/umbraco-aad-members-signin?scheme=ActiveDirectory");
<div class="login-microsoft">
<a class="btn btn-primary" href="@externalLoginUrl">
Sign in with Microsoft
</a>
</div>
}
Just to get to the routeSander L
01/22/2025, 9:29 AMSander L
01/22/2025, 9:32 AMOlti
01/22/2025, 9:57 AMOlti
01/22/2025, 10:00 AMSander L
01/22/2025, 10:01 AMOlti
01/22/2025, 10:06 AMOlti
01/22/2025, 10:06 AMkdx-perbol
01/23/2025, 8:00 AMmemberExternalLoginProviders.GetMemberProvidersAsync
stuff. I'd say the guide is pretty useless without it. Aside from the other stuff in there that's confusing. I know, submit an issue, etc. 🙂Sander L
01/23/2025, 11:51 AM