如何使用 google auth 返回的 id_token 来访问服务器 API

问题描述 投票:0回答:1

我有一个移动和服务器端(带有 API 的 Web)应用程序,它使用 Identity 来对用户进行身份验证。移动应用程序在服务器上打开一个 WebView,一旦用户成功登录,就会使用从服务器发回的访问令牌重新打开移动应用程序。然后,它可以利用服务器端需要 Bearer 令牌身份验证的 API。

此模式的解释请参见此处

enter image description here

使用移动应用程序时,如果用户通过用户名/密码登录,则持有者令牌将起作用。如果用户通过 google 登录,API 将返回 401(访问被拒绝)错误。这是我的身份验证设置代码:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
    options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
    options.DefaultSignInScheme = IdentityConstants.ApplicationScheme;
})
// I tried .AddGoogle here previously, but this is the only way I've been able to return an id_token. The access_token is for google API's, not internal API's
.AddOpenIdConnect(
    GoogleDefaults.AuthenticationScheme,
    GoogleDefaults.DisplayName, o =>
{
    var googleAuthNSection = configuration.GetSection("Authentication:Google");
    o.SignInScheme = IdentityConstants.ExternalScheme;
    o.Authority = "https://accounts.google.com";
    o.ClientId = googleAuthNSection["ClientId"]!;
    o.ClientSecret = googleAuthNSection["ClientSecret"]!;
    o.ResponseType = OpenIdConnectResponseType.IdToken;
    o.CallbackPath = "/signin-google";
    o.SaveTokens = true;
    o.Scope.Add("email");
})
.AddCookie(IdentityConstants.ApplicationScheme, o =>
{
    o.LoginPath = new PathString("/Account/Login");
    o.Events = new CookieAuthenticationEvents
    {
        OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
    };
})
.AddBearerToken(IdentityConstants.BearerScheme, o =>
{
    o.BearerTokenExpiration = TimeSpan.FromHours(5);
})
.AddExternalCookie();

这里是如果用户使用用户名/密码登录则获取不记名令牌的代码。请注意,我发布到 /login 端点以获取带有当前用户电子邮件/密码的不记名令牌,因为我可以在此处访问它。使用谷歌登录时,我没有电子邮件/密码,因此无法重新验证身份端点以获取不记名令牌。

    var body = new { email, password };
    var content = JsonSerializer.Serialize(body);
    var buffer = Encoding.UTF8.GetBytes(content);
    var byteContent = new ByteArrayContent(buffer);
    byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    var client = httpClientFactory.CreateClient(MealPlanApi.ApiClient);
    var loginResponse = await client.PostAsync("/login", byteContent);
    var contents = await loginResponse.Content.ReadAsStringAsync();
    var doc = JsonSerializer.Deserialize<JsonElement>(contents);
    var accessToken = doc.GetProperty("accessToken").GetString()!;
    await RedirectToMobileAuthenticatedAsync(context, user, accessToken, returnUrl);

以下是登录第 3 方身份验证并将令牌发送回移动应用程序的代码。用户显然已通过身份验证,但此处获取的访问令牌无法用于访问 API。

[Route("mobileauth")]
[ApiController]
public class AuthController : ControllerBase
{
    const string callbackScheme = "MyCallbackScheme";

    [HttpGet("{scheme}")] // eg: Microsoft, Facebook, Apple, etc
    public async Task Get([FromRoute] string scheme)
    {
        var auth = await Request.HttpContext.AuthenticateAsync(scheme);

        if (!auth.Succeeded
            || auth?.Principal == null
            || !auth.Principal.Identities.Any(id => id.IsAuthenticated)
            || string.IsNullOrEmpty(auth.Properties.GetTokenValue("access_token")))
        {
            // Not authenticated, challenge
            await Request.HttpContext.ChallengeAsync(scheme);
        }
        else
        {
            var claims = auth.Principal.Identities.FirstOrDefault()?.Claims;
            var email = string.Empty;
            email = claims?.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;

            // Get parameters to send back to the callback
            var qs = new Dictionary<string, string>
            {
                { "access_token", auth.Properties.GetTokenValue("access_token") },
                { "refresh_token", auth.Properties.GetTokenValue("refresh_token") ?? string.Empty },
                { "expires", (auth.Properties.ExpiresUtc?.ToUnixTimeSeconds() ?? -1).ToString() },
                { "email", email }
            };

            // Build the result url
            var url = callbackScheme + "://#" + string.Join(
                "&",
                qs.Where(kvp => !string.IsNullOrEmpty(kvp.Value) && kvp.Value != "-1")
                .Select(kvp => $"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}"));

            // Redirect to final url
            Request.HttpContext.Response.Redirect(url);
        }
    }
}

这可能是我保护和使用端点的方式存在问题吗?

在主程序中

app.MapControllers();

控制器

[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = "Identity.Bearer,Identity.External,Google")]
[ApiController]
public class MyController() : ControllerBase

这是我来自 HttpContext.AuthenticateAsync("Google") 的回复

enter image description here

注意我已通过身份验证。当前用户可以访问网络上的资源。但是,如果我将此访问令牌或 ID 令牌传递给移动应用程序,则它不可用于 API。

asp.net google-oauth asp.net-identity
1个回答
0
投票
网络需要

.AddOpenIdConnect
(进行身份验证)

API 需要

.AddJwtBearer
(以提供对 API 的访问权限)

如果这是两个独立的应用程序,它们将进入两个独立的初创公司。就我而言,Web 应用程序和 API 在同一个服务中运行,因此它们是在一起的。

.AddOpenIdConnect(
    GoogleDefaults.AuthenticationScheme,
    GoogleDefaults.DisplayName, o =>
{
    var googleAuthNSection = configuration.GetSection("Authentication:Google");
    o.SignInScheme = IdentityConstants.ExternalScheme;
    o.Authority = "https://accounts.google.com";
    o.ClientId = googleAuthNSection["ClientId"]!;
    o.ClientSecret = googleAuthNSection["ClientSecret"]!;
    o.ResponseType = OpenIdConnectResponseType.CodeToken;
    o.CallbackPath = "/signin-google";
    o.SaveTokens = true;
    o.Scope.Add("email");
})
.AddJwtBearer(o =>
{
    var googleAuthNSection = configuration.GetSection("Authentication:Google");
    var googleIssuer = "https://accounts.google.com";
    var googleClientId = googleAuthNSection["ClientId"]!;
    o.Authority = googleIssuer;
    o.Audience = googleClientId;
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false,
        ValidateAudience = false,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = false,
        ValidIssuer = googleIssuer,
        ValidAudience = googleClientId,
    };
})

然后使用“承载”方案来保护 API

[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = "Bearer")]
[ApiController]
public class MyController : ControllerBase
© www.soinside.com 2019 - 2024. All rights reserved.