Reputation: 23
I'm getting the error InvalidOperationException: Cannot redirect to the authorization endpoint, the configuration may be missing or invalid.
Prior to this I had the error that says something like "authority, metaaddress, configuration or configuration manager is missing". I feel this is important. So, what I had:
/// <summary>
/// Configures OpenID Connect authentication.
/// </summary>
/// <param name="builder">The <see cref="IPersonalIdentityServerBuilder"/> object.</param>
/// <returns>The <see cref="IPersonalIdentityServerBuilder"/>.</returns>
public static IPersonalIdentityServerBuilder AddOpenIdConnectAuthentication(this IPersonalIdentityServerBuilder builder)
{
IServiceCollection _services = builder?.Services ?? throw new ArgumentNullException(nameof(builder));
_services
.AddAuthentication(opt => opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme)
.AddOpenIdConnect();
_services.AddSingleton<IPostConfigureOptions<OpenIdConnectOptions>, ConfigureOpenIdConnectOptions>();
return builder;
}
I then had the following class that configures the options
/// <summary>
/// Configuration class for the <see cref="OpenIdConnectOptions"/>.
/// </summary>
internal class ConfigureOpenIdConnectOptions :
IPostConfigureOptions<OpenIdConnectOptions>
{
/// <summary>
/// My personal OpenId options.
/// </summary>
private readonly IOptions<PersonalIentityServerOpenIdOptions> _openIdOptions;
/// <summary>
/// The class that has the events for OpenId authentication.
/// </summary>
private readonly OpenIdNotificationEventHandler _eventHandler;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureOpenIdConnectOptions"/> class.
/// </summary>
/// <param name="openIdOptions">The personal OpenId options.</param>
/// <param name="eventHandler">The handler for OpenId authentication events.</param>
public ConfigureOpenIdConnectOptions(
IOptions<PersonalIdentityServerOpenIdOptions> openIdOptions,
OpenIdNotificationEventHandler eventHandler)
{
this._openIdOptions = openIdOptions ?? throw new ArgumentNullException(nameof(openIdOptions));
this._eventHandler = eventHandler ?? throw new ArgumentNullException(nameof(eventHandler));
}
/// <summary>
/// Configures the options.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="options">The options to configure.</param>
public void PostConfigure(string name, OpenIdConnectOptions options)
{
PersonalIdentityServerOpenIdOptions _opt = this._openIdOptions.Value;
options.Authority = _opt.Authority;
options.ClientId = _opt.ClientId;
options.ClientSecret = _opt.Secret;
options.MetadataAddress = "/" + ProtocolPath.Discovery;
options.ProtocolValidator.RequireNonce = true;
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
};
// Keeps id_token smaller
options.GetClaimsFromUserInfoEndpoint = true;
// The callback paths require a relative URL. This was a change that Microsoft made in the .NET Core version. We, however,
// support an absolute URL. Attempting to set the callback paths to an absolute URL will cause an exception to be thrown.
// Therefore, we will now support either. If it's an absolute URL then it gets set in the RedirectToIdentityProvider
// event. If it's a relative URL, we'll set it here.
if (!_opt.SetLowLevelRedirectUri) { options.CallbackPath = _opt.RedirectUri; }
if (!_opt.SetLowLevelPostLogoutRedirectUri) { options.SignedOutCallbackPath = _opt.PostLogoutRedirectUri; }
_opt.Scopes.ForEach(s => options.Scope.Add(s));
options.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = this._eventHandler.AuthorizationCodeRecieved,
OnTokenResponseReceived = this._eventHandler.TokenResponseReceived,
OnTokenValidated = this._eventHandler.TokenValidated,
OnRedirectToIdentityProvider = this._eventHandler.RedirectToIdentityProvider,
};
}
}
I ended up, after much decompiling and Google searching, added the following line to the AddOpenIdConnectAuthentication method defined above. This is Microsofts configuration for the open ID settings.
_services.AddSingleton<IPostConfigureOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureOptions>();
This resolved that issue but now I have the one in the title. From looking at the source, it looks like it's because the IssuerAddress is empty. For the life me I can't understand why. Furthermore, I don't understand why I was required to add the configuration for Microsofts own class to get it to work.
I know that I can get around it somehow and will likely find a workaround, but I just don't think it should be this hard.
If anyone has any thoughts, they would be most welcome.
I wanted to expand on some of the things that I found and ended up changing due to the time I spent on this. I want to give a special thank you to https://stackoverflow.com/users/68490/tore-nestenius below.
I ended up changing from using IPostConfigure<OpenIdConnectOptions>
to IConfigure<OpenIdConnectOptions>
. When doing this, it seems to go through the Configure(name, options)
for IConfigureNamedOptions<OpenIdConnectOptions>
not the Configure(options)
for IConfigureOptions<OpenIdConnectOptions>
. Due to this, I implemented both. I don't know if you have to, because I didn't try all possible combinations, but doing so worked.
When using IPostConfigurationOptions<OpenIdConnectOptions>
, you must add it to the service collection before the call to AddOpenIdConnect()
.
I also removed the line to manually register the Microsofts IPostConfigreOptions
, this is not needed.
Upvotes: 0
Views: 4981
Reputation: 5835
When I added a new application to my B2C I set the redirect url incorrectly and when I set it to the correct one I still received this error no matter what I changed in code.
Just by chance I decided to run the UserFlow in the B2C portal using the new application and once I did that it began working locally. Not sure why it fixed it, maybe there is some caching on the AD side and that invalidated it to get the correct redirect uri.
Upvotes: 0
Reputation: 19981
The metadata address should be an absolute address starting with https:// and not a relative URL as in your code:
options.MetadataAddress = "/" + ProtocolPath.Discovery;
then I am a bit confused why you need o add your own ConfigureOpenIdConnectOptions class? To configure the openid-connect I simply:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/User/Login";
options.LogoutPath = "/User/Logout";
options.AccessDeniedPath = "/User/AccessDenied";
}).AddMyTestOpenIdConnect(options =>
{
options.Authority = "https://localhost:6001";
options.ClientId = "authcodeflowclient";
options.ClientSecret = "mysecret";
options.ResponseType = "code";
...
});
If the MetaData URL is on HTTP, you might need to set
options.RequireHttpsMetadata = false;
But if you set the Authority field, then I doubt you need to also set the MetaDataAddress.
Upvotes: 1