Quantcast
Channel: Customized Commerce 13 and earlier versions
Viewing all articles
Browse latest Browse all 9642

Setup Service API Authentication for Azure AD (Entra)

$
0
0

Hello,

Although I looked in several places, I'm sure that I'm missing a simple piece of information, but at this moment I totally don't understand what am I missing.

First, I setup the commerce website without any issues and without any problems - everything is working fine. I even configured the Azure AD authentication for the administration interface according to the official available guides - everything is working.

Second, I used the Service API to retrieve content information and it's working without any issues:

https://localhost:5000/api/episerver/v3.0/content/18

Then, though I configured the commerce API to disable scope validation (like I did with the content delivery), it seems that I'm not able to call any commerce API URLs being unauthenticated:

https://localhost:5000/episerverapi/commerce/catalogs

This returns 401 Unauthorized:

{
    "message": "User is not authorized for this request."
}

Now, the problem is that I try to authenticate the user first by obtaining the token using this URL:

https://localhost:5000/api/episerver/connect/token

And here is the issue that I have and cannot fix:

System.InvalidOperationException: No service for type 'EPiServer.OpenIDConnect.Internal.OpenIDConnectSignInManager' has been registered.
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at EPiServer.OpenIDConnect.Controllers.Internal.AuthorizationController..ctor()
   at lambda_method14(Closure, IServiceProvider, Object[])
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass6_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()

And this is how I configured the authentication of the Service API:

        services.ConfigureContentApiOptions(options =>
        {
            options.EnablePreviewFeatures = true;
            options.IncludeEmptyContentProperties = true;
            options.FlattenPropertyModel = false;
            options.IncludeMasterLanguage = false; 
        });
        services.AddContentDeliveryApi(JwtBearerDefaults.AuthenticationScheme, options => {
                options.SiteDefinitionApiEnabled = true;
            })
            .WithFriendlyUrl()
            .WithSiteBasedCors();
        services.AddCommerceApi<ApplicationUser>(OpenIDConnectOptionsDefaults.AuthenticationScheme, o =>
        {
            o.DisableScopeValidation = true;
        });
        // Content Definitions API
        services.AddContentDefinitionsApi(options =>
        {
            // Accept anonymous calls
            options.DisableScopeValidation = true;
        });
        // Content Management
        services.AddContentManagementApi(JwtBearerDefaults.AuthenticationScheme, options =>
        {
            // Accept anonymous calls
            options.DisableScopeValidation = true;
        });
        // Service API configuration
        services.AddServiceApiAuthorization(JwtBearerDefaults.AuthenticationScheme);

And this is how I configured the OpenIdConnect:

services
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = "azure-cookie";
                options.DefaultChallengeScheme = "azure";
            })
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme,
                options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = "https://login.microsoftonline.com/" +
                                      this.configurationProvider["Authentication:AzureTenantId"] + "/v2.0",
                        ValidateIssuer = true,
                        ValidAudience = this.configurationProvider["Authentication:AzureClientId"],
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        // IssuerSigningKeys = oidcConfig.SigningKeys,
                        ValidateIssuerSigningKey = true
                    };
                })
            .AddCookie("azure-cookie", options =>
            {
                options.Events.OnSignedIn = async ctx =>
                {
                    if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
                    {
                        // Syncs user and roles so they are available to the CMS
                        var synchronizingUserService = ctx
                            .HttpContext
                            .RequestServices
                            .GetRequiredService<ISynchronizingUserService>();
                        await synchronizingUserService.SynchronizeAsync(claimsIdentity);
                    }
                };
            })
            .AddOpenIdConnect("azure", options =>
            {
                options.SignInScheme = "azure-cookie";
                options.SignOutScheme = "azure-cookie";
                options.ResponseType = OpenIdConnectResponseType.Code;
                options.CallbackPath = "/signin-oidc";
                options.ClientSecret = this.configurationProvider["Authentication:AzureAppSecret"];
                options.UsePkce = true;
                // If Azure AD is register for multi-tenant
                //options.Authority = "https://login.microsoftonline.com/" + "common" + "/v2.0";
                options.Authority = "https://login.microsoftonline.com/" +
                                    this.configurationProvider["Authentication:AzureTenantId"] + "/v2.0";
                options.ClientId = this.configurationProvider["Authentication:AzureClientId"];
                options.Scope.Clear();
                options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
                options.Scope.Add(OpenIdConnectScope.OfflineAccess);
                options.Scope.Add(OpenIdConnectScope.Email);
                options.MapInboundClaims = false;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    RoleClaimType = "roles",
                    NameClaimType = "preferred_username",
                    ValidateIssuer = false
                };
                options.Events.OnRedirectToIdentityProvider = ctx =>
                {
                    // Prevent redirect loop
                    if (ctx.Response.StatusCode == 401)
                    {
                        ctx.HandleResponse();
                    }
                    return Task.CompletedTask;
                };
                options.Events.OnAuthenticationFailed = context =>
                {
                    context.HandleResponse();
                    context.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(context.Exception.Message));
                    return Task.CompletedTask;
                };
            });

Can you please help me a bit understanding what am I missing?

Thank you very much.


Viewing all articles
Browse latest Browse all 9642

Trending Articles