首页 > 解决方案 > 在重定向到其他 URL 并且没有身份验证的情况下在 cookie 上存储声明

问题描述

我只需要建议这是否可行。我正在为我的 Shopify 应用程序开发授权,并且我需要存储来自 shopify auth 的访问令牌,以便将来验证我的前端应用程序。

所以 shopify 调用的第一个端点是这个:

    [HttpGet("install")]
    public async Task<IActionResult> Install()
    {
        try
        {
            if (ModelState.IsValid)
            {
                var queryString = Request.QueryString.Value;
                var isValid = _shopifyService.VerifyRequest(queryString);

                if (isValid)
                {
                    var shopifyUrl = Request.Query["shop"];
                    var authUrl = _shopifyService.BuildAuthUrl(shopifyUrl,
                        $"{Request.Scheme}://{Request.Host.Value}/api/shopify/authorize",
                        Program.Settings.Shopify.AuthorizationScope);

                    return Redirect(authUrl);
                }
            }
        }
        catch (Exception ex)
        {
            var exceptionMessage = await ApiHelpers.GetErrors(ex, _localizer).ConfigureAwait(false);
            ModelState.AddModelError(new ValidationResult(exceptionMessage));
        }

        ModelState.AddModelError(new ValidationResult(_localizer["InvalidAuthStore"]));
        return BadRequest(ModelState.GetErrors());
    }

这工作正常,这个 api 调用的结果实际上将重定向到我的 api 的相同链接,但是这个将授权应用程序:

    [HttpGet("authorize")]
    public async Task<IActionResult> AuthorizeStore()
    {
        try
        {
            if (ModelState.IsValid)
            {
                var code = Request.Query["code"];
                var shopifyUrl = Request.Query["shop"];

                var accessToken = await _shopifyService.AuthorizeStore(code, shopifyUrl).ConfigureAwait(false);

                var identity = User.Identity as ClaimsIdentity;
                identity.AddClaim(new Claim(Constants.Claims.AccessToken, accessToken));

                // genereate the new ClaimsPrincipal
                var claimsPrincipal = new ClaimsPrincipal(identity);

                // store the original tokens in the AuthenticationProperties
                var props = new AuthenticationProperties {
                    AllowRefresh = true,
                    ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1),
                    IsPersistent = false,
                    IssuedUtc = DateTimeOffset.UtcNow,
                };

                // sign in using the built-in Authentication Manager and ClaimsPrincipal
                // this will create a cookie as defined in CookieAuthentication middleware
                await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, props).ConfigureAwait(false);


                Uri uri = new Uri($"{Program.Settings.Shopify.RedirectUrl}?token={accessToken}");
                return Redirect(uri.ToString());
            }
        }
        catch (Exception ex)
        {
            var exceptionMessage = await ApiHelpers.GetErrors(ex, _localizer).ConfigureAwait(false);
            ModelState.AddModelError(new ValidationResult(exceptionMessage));
        }

        ModelState.AddModelError(new ValidationResult(_localizer["InvalidAuthStore"]));
        return BadRequest(ModelState.GetErrors());
    }

所以上面的 api 将在 shopify 中授权我的应用程序并返回一个访问令牌。accessToken 是我要保存在具有 Cookie 身份验证类型的声明身份中的那个(这没有授权用户凭据)。此时仍然没有错误,在调用HttpContext.SignInAsync函数后,我仍然可以使用调试器查看新添加的声明。

如您在代码中所见,在分配声明后,我调用以将应用程序重定向到前端链接(注意:前端和后端具有不同的 url)

在我的前端应用程序中,我有一个 Nuxt 中间件,我放置了一个逻辑来检查从后端接收到的令牌,因为我只使用查询参数将令牌传递给前端应用程序。这是我的中间件代码:

export default function ({ app, route, next, store, error, req }) {
  if (process.browser) {
    const shopifyAccessToken = store.get('cache/shopifyAccessToken', null)

    if (!shopifyAccessToken && route.query.token) {
      // if has token on query params but not yet in cache, store token and redirect
      store.set('cache/shopifyAccessToken', route.query.token)
      app.router.push({
        path: '/',
        query: {}
      })

      // verify access token on the route
      app.$axios
        .get(`/shopify/verifyaccess/${route.query.token}`)
        .catch((err) => {
          error(err)
        })
    } else if (!shopifyAccessToken && !route.query.token) {
      // if does not have both, throw error
      error({
        statusCode: 401,
        message: 'Unauthorized access to this app'
      })
    }
  } else {
    next()
  }
}

在我的中间件中,当路由的查询参数等于时,token=它会调用另一个 api 来验证保存在我的声明身份中的 accessToken:

    [HttpGet("verifyaccess/{accessToken}")]
    public async Task<IActionResult> VerifyAccess(string accessToken)
    {
        try
        {
            if (ModelState.IsValid)
            {
                var principal = HttpContext.User;
                if (principal?.Claims == null)
                    return Unauthorized(_localizer["NotAuthenticated"]);

                var accessTokenClaim = principal.FindFirstValue(Constants.Claims.AccessToken);

                if (accessToken == accessTokenClaim)
                {
                    return Ok();
                } 
                else
                {
                    return Unauthorized(_localizer["NotAuthenticated"]);
                }
            }
        }
        catch (Exception ex)
        {
            var exceptionMessage = await ApiHelpers.GetErrors(ex, _localizer).ConfigureAwait(false);
            ModelState.AddModelError(new ValidationResult(exceptionMessage));
        }

        ModelState.AddModelError(new ValidationResult(_localizer["InvalidAuthStore"]));
        return BadRequest(ModelState.GetErrors());
    }

查看上面的代码,它总是让我失败,因为我保存在authorize端点上的声明身份不存在,或者简而言之,ClaimsIdentity 总是空的。

以下是我注册 Cookie 配置的方法:

    private void ConfigureAuthCookie(IServiceCollection services)
    {
        services.AddAuthentication(option =>
        {
            option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            option.RequireAuthenticatedSignIn = false;
        })
        .AddCookie(options => {
            options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
            options.SlidingExpiration = true;
            options.Cookie.Name = "shopifytoken";
        });

        services.Configure<CookiePolicyOptions>(options =>
        {
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });
    }

我还在我的 Startup.Configure 上放了一个app.UseAuthentication()andapp.UseAuthorization()

如果这看起来令人困惑,请告诉我,以便我进行修改。我的主要目标是能够访问我保存在 ClaimsIdentity 中的 accessToken,以便验证令牌。我这样做的原因是因为目前 shopify 没有用于验证访问令牌的 API。因此,当用户像这样访问我的应用程序链接时,http://example.com/?token=<any incorrect token>他们已经可以访问我的应用程序。

标签: asp.net-corenuxt.jsasp.net-core-3.1claims-based-identityclaims

解决方案


推荐阅读