XAF WebApi- google oauth2

47 Views Asked by At

I tried to implement oauth2 for XAF WebApi for Google but unfortunately it doesn't work at all.

The authorization seems to pass, but it does not enter the Authenticate method in the CustomAuthenticationProvider class.

When I use AzureAd oauth2 everything works perfectly and when executing the endpoint I get the public object Authenticate(IObjectSpace objectSpace) method from the CustomAuthenticationOAuthProvider class.

Unfortunately, this is not the case with Google...

Could you tell me what I'm doing wrong?

I will be very grateful for any hint.

Startup:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }


    public void ConfigureServices(IServiceCollection services)
    {
        List<string> authenticationSchemas = new List<string> { JwtBearerDefaults.AuthenticationScheme };

        
        services.AddScoped<IAuthenticationTokenProvider, JwtTokenProviderService>();

        services.AddXafWebApi(builder =>
        {
            builder.AddXpoServices();

            
            builder.ObjectSpaceProviders
                .AddSecuredXpo((serviceProvider, options) =>
                {
                    string connectionString = null;
                    if (Configuration.GetConnectionString("ConnectionString") != null)
                    {
                        connectionString = Configuration.GetConnectionString("ConnectionString");
                    }
                    ArgumentNullException.ThrowIfNull(connectionString);
                    options.ConnectionString = connectionString;
                    options.ThreadSafe = true;
                    options.UseSharedDataStoreProvider = true;
                })
                .AddNonPersistent();

            builder.Security
                .UseIntegratedMode(options =>
                {
                    ....
                })
                .AddPasswordAuthentication(options =>
                {
                    options.IsSupportChangePassword = true;
                })
                .AddAuthenticationProvider<CustomAuthenticationOAuthProvider>();

            
        }, Configuration);

        services
            .AddControllers()
            .AddOData((options, serviceProvider) =>
            {
                options
                    .AddRouteComponents("api/odata", new EdmModelBuilder(serviceProvider).GetEdmModel())
                    .EnableQueryFeatures(100);
            });

        var authentication = services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme);

        authentication.AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuerSigningKey = true,
                //ValidIssuer = Configuration["Authentication:Jwt:Issuer"],
                //ValidAudience = Configuration["Authentication:Jwt:Audience"],
                ValidateIssuer = false,
                ValidateAudience = false,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authentication:Jwt:IssuerSigningKey"]))
            };
        });


        if (Configuration.GetSection("Authentication:Google").Exists())
        {
            authenticationSchemas.Add(GoogleDefaults.AuthenticationScheme);

            
            authentication.AddGoogle(options =>
                {
                    Configuration.Bind("Authentication:Google", options);
                    //options.AuthorizationEndpoint += "?prompt=consent";
                    //options.SignInScheme = GoogleDefaults.AuthenticationScheme;
                    options.ClaimActions.MapJsonKey(XafClaimTypes.UserImageUrl, "picture");
                });


        }

        services.AddAuthorization(options =>
        {
            options.DefaultPolicy = new AuthorizationPolicyBuilder(authenticationSchemas.ToArray())
                    .RequireAuthenticatedUser()
                    .RequireXafAuthentication()
                    .Build();

        });

        services.AddSwaggerGen(c =>
        {
            c.EnableAnnotations();
            c.SwaggerDoc("v1", new OpenApiInfo
            {
                Title = "IApi",
                Version = "v1",
                Description = @"API. Istnieje możliwość parametryzowania ciągu zapytania. Szczegóły: <a href='https://docs.microsoft.com/en-us/odata/concepts/queryoptions-overview'>Dostępne opcje zapytań</a>"
            });
            c.AddSecurityDefinition("JWT", new OpenApiSecurityScheme()
            {
                Type = SecuritySchemeType.Http,
                Name = "Bearer",
                Scheme = "bearer",
                BearerFormat = "JWT",
                In = ParameterLocation.Header
            });
            c.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme() {
                            Reference = new OpenApiReference() {
                                Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
                                Id = "JWT"
                            }
                        },
                        new string[0]
                    },
            });

            if (Configuration.GetSection("Authentication:Google").Exists())
            {
                var scopes = Configuration.GetSection("Authentication:Google:Scopes").Get<Dictionary<string, string>>().ToDictionary(x => x.Value, x => x.Key); 

                c.AddSecurityDefinition(GoogleDefaults.AuthenticationScheme, new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Flows = new OpenApiOAuthFlows()
                    {
                        AuthorizationCode = new OpenApiOAuthFlow()
                        {
                            AuthorizationUrl = new Uri($"{GoogleDefaults.AuthorizationEndpoint}"),
                            TokenUrl = new Uri($"{GoogleDefaults.TokenEndpoint}"),
                            Scopes = scopes
                        }
                    }
                });


                c.AddSecurityRequirement(new OpenApiSecurityRequirement() {
                {
                    new OpenApiSecurityScheme {
                        Reference = new OpenApiReference {
                            Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
                            Id = GoogleDefaults.AuthenticationScheme
                        },
                        In = ParameterLocation.Header
                    },
                    new string[0]
                }
                });
            }

        });

    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. To change this for production scenarios, see: https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "Api v1");

            if (Configuration.GetSection("Authentication:Google").Exists())
            {
                c.OAuthClientId(Configuration["Authentication:Google:ClientId"]);
                c.OAuthClientSecret(Configuration["Authentication:Google:ClientSecret"]);
            }

            c.OAuthUsePkce();
        });

        app.UseHttpsRedirection();
        app.UseRequestLocalization();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapXafEndpoints();
        });
    }
}

CustomAuthenticationOAuthProvider:

public class CustomAuthenticationOAuthProvider : IAuthenticationProviderV2
{
    private readonly IPrincipalProvider principalProvider;

    public CustomAuthenticationOAuthProvider(IPrincipalProvider principalProvider)
    {
        this.principalProvider = principalProvider;
    }

    public object Authenticate(IObjectSpace objectSpace)
    {
        if (!CanHandlePrincipal(principalProvider.User))
        {
            return null;
        }

        ClaimsPrincipal claimsPrincipal = (ClaimsPrincipal)principalProvider.User;
        var userEmailClaim = claimsPrincipal.FindFirst(ClaimTypes.Email)?.Value;

        var user = (IAuthenticationUser)objectSpace.FindObject(SecuritySystem.UserType, CriteriaOperator.Parse("Lower(Email) = ?", userEmailClaim.ToLower()));
        if(user != null)
        {
            return user;
        }

        return null;
    }

    private bool CanHandlePrincipal(IPrincipal user)
    {
        return user.Identity.IsAuthenticated &&
            user.Identity.AuthenticationType != SecurityDefaults.Issuer &&
            user.Identity.AuthenticationType != SecurityDefaults.PasswordAuthentication &&
            user.Identity.AuthenticationType != SecurityDefaults.WindowsAuthentication &&
            !(user is WindowsPrincipal);
    }
 
}
0

There are 0 best solutions below