探索Auth0 ASP.NET Core认证SDK

290 阅读5分钟

Auth0为您带来了新的ASP.NET Core Authentication SDK,它改善了您的开发体验,并与Auth0的功能更好地整合在一起!让我们来参观一下SDK的主要功能,看看将你的ASP.NET应用程序与Auth0整合起来变得多么容易。

SDK概述

到目前为止,你已经习惯于依靠标准的 Microsoft.AspNetCore.Authentication.OpenIdConnect包来整合你的ASP.NET应用程序和Auth0。那个库很好地完成了它的工作。然而,当你脱离了实现简单的登录和注销时,你的代码开始变得冗长,直到变得混乱。

你可以通过用C#扩展方法实现你的认证来缓解这个问题。但是,既然Auth0的专家可以为你提供一个随时可用的SDK,为什么还要自己动手呢?

ASP.NET核心认证SDK允许你写更少的代码,将Auth0认证和授权整合到你的ASP.NET应用程序中。它简化了配置,支持额外的查询参数和刷新令牌,自然支持Auth0组织和用户邀请。它实际上是一个围绕 Microsoft.AspNetCore.Authentication.OpenIdConnect库,但它为你带来了一个标准和简单的方法,将Auth0集成到你的应用程序中。

您可以将SDK用于您所有需要认证的ASP.NET应用程序,如ASP.NET MVC应用程序、Razor Web应用程序和Blazor服务器应用程序。

安装ASP.NET核心认证SDK

你可以通过使用Visual Studio中的NuGet Package Manager UI来安装Auth0 ASP.NET Core SDK,如下图所示。

Installing the Auth0 ASP.NET SDK with NuGet Package Manager UI

你也可以通过在包管理器控制台运行以下命令来安装它。

Install-Package Auth0.AspNetCore.Authentication

或者你可以在终端窗口中使用以下命令。

dotnet add package Auth0.AspNetCore.Authentication

无论你选择哪种方式,你都会在你的ASP.NET项目中得到这个 Auth0.AspNetCore.Authentication在你的ASP.NET项目中的依赖性。

基本登录用例

让我们开始看看新的SDK是如何改善你的开发者体验的。当然,你需要一个Auth0账户来将你的ASP.NET应用程序与Auth0集成。如果你还没有,你可以现在就注册一个免费的。按照Auth0 ASP.NET Core Authentication SDK的快速入门,注册你的应用程序并获得你的应用程序密钥

下面是你写的典型代码,在你的ASP.NET应用程序中启用认证,当使用 Microsoft.AspNetCore.Authentication.OpenIdConnect:

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuthentication(options => {
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  })
  .AddCookie()
  .AddOpenIdConnect("Auth0", options => {
    options.Authority = $"https://{Configuration["Auth0:Domain"]}";
    options.ClientId = Configuration["Auth0:ClientId"];
    options.CallbackPath = new PathString("/callback");
    options.TokenValidationParameters = new TokenValidationParameters
    {
      NameClaimType = "name"
    };
    options.Events = new OpenIdConnectEvents
    {
      OnRedirectToIdentityProviderForSignOut = (context) =>
      {
        var logoutUri = $"https://{Configuration["Auth0:Domain"]}/v2/logout?client_id={Configuration["Auth0:ClientId"]}";

        var postLogoutUri = context.Properties.RedirectUri;
        if (!string.IsNullOrEmpty(postLogoutUri))
        {
          if (postLogoutUri.StartsWith("/"))
          {
            // transform to absolute
            var request = context.Request;
            postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
          }
          logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
        }

        context.Response.Redirect(logoutUri);
        context.HandleResponse();

        return Task.CompletedTask;
      }
    };
  });
}

我不会通过该代码来解释每一个步骤。你只需要知道它通过依靠Auth0通用登录页面在你的应用程序中实现登录和注销功能。

通过使用Auth0 ASP.NET Core Authentication SDK,你的代码会缩短如下。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuth0WebAppAuthentication(options =>
  {
    options.Domain = Configuration["Auth0:Domain"];
    options.ClientId = Configuration["Auth0:ClientId"];
  });
}

令人印象深刻,不是吗?😲

该SDK处理了所有你以前需要手动完成的配置工作。而这仅仅是个开始!

获取访问令牌

如果你想从你的ASP.NET应用程序中调用一个API呢?在这种情况下,你需要向Auth0请求一个特定受众的访问令牌

在没有SDK的情况下,要做到这一点,需要类似于下面的代码。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuthentication(options => {
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  })
  .AddCookie()
  .AddOpenIdConnect("Auth0", options => {
    options.Authority = $"https://{Configuration["Auth0:Domain"]}";
    options.ClientId = Configuration["Auth0:ClientId"];
    //👇new code
    options.ClientSecret = Configuration["Auth0:ClientSecret"];
    options.SaveTokens = true;
    options.ResponseType = OpenIdConnectResponseType.Code;
    //👆new code
    options.CallbackPath = new PathString("/callback");
    options.TokenValidationParameters = new TokenValidationParameters
    {
      NameClaimType = "name"
    };
    options.Events = new OpenIdConnectEvents
    {
      OnRedirectToIdentityProviderForSignOut = (context) =>
      {
        var logoutUri = $"https://{Configuration["Auth0:Domain"]}/v2/logout?client_id={Configuration["Auth0:ClientId"]}";

        var postLogoutUri = context.Properties.RedirectUri;
        if (!string.IsNullOrEmpty(postLogoutUri))
        {
          if (postLogoutUri.StartsWith("/"))
          {
            // transform to absolute
            var request = context.Request;
            postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
          }
          logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
        }

        context.Response.Redirect(logoutUri);
        context.HandleResponse();

        return Task.CompletedTask;
      }
      //👇new code
      OnRedirectToIdentityProvider = context =>
      {
        context.ProtocolMessage.SetParameter("audience", Configuration["Auth0:Audience"]);
        return Task.FromResult(0);
      }
      //👆new code
    };
  });
}

基本上,你需要在之前的代码中多加几行,以便改变OAuth流程,提供客户端秘密,启用令牌保存,并将受众值附加到授权请求中。

使用SDK实现相同结果的等效代码如下。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuth0WebAppAuthentication(options =>
  {
    options.Domain = Configuration["Auth0:Domain"];
    options.ClientId = Configuration["Auth0:ClientId"];
    options.ClientSecret = Configuration["Auth0:ClientSecret"];
  })
  .WithAccessToken(options =>
  {
    options.Audience = Configuration["Auth0:Audience"];
  }); 
}

你对此不感到兴奋吗?💥你的代码将更简洁、更干净、更清晰

刷新令牌的魔力

万一你需要一个刷新令牌呢?🤔没有什么比这更简单了。只要设置一个标志。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuth0WebAppAuthentication(options =>
  {
    options.Domain = Configuration["Auth0:Domain"];
    options.ClientId = Configuration["Auth0:ClientId"];
    options.ClientSecret = Configuration["Auth0:ClientSecret"];
  })
  .WithAccessToken(options =>
  {
    options.Audience = Configuration["Auth0:Audience"];
    options.UseRefreshTokens = true;   //👈 new code
  });
}

将其与你需要添加到无SDK代码中的代码进行比较。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuthentication(options => {
  //...the code to configure authentication...
  })
  .AddCookie(options => {
    options.Events.OnValidatePrincipal = (context) => {
    var options = context.HttpContext.RequestServices.GetRequiredService<Auth0Options>();

    string accessToken;
    if (context.Properties.Items.TryGetValue(".Token.access_token", out accessToken))
    {
      if (options.UseRefreshTokens)
      {
        string refreshToken;
        if (context.Properties.Items.TryGetValue(".Token.refresh_token", out refreshToken))
        {
          var now = DateTimeOffset.Now;
          var expiresAt = DateTimeOffset.Parse(context.Properties.Items[".Token.expires_at"]);
          var leeway = 60;
          var difference = DateTimeOffset.Compare(expiresAt, now.AddSeconds(leeway));
          var isExpired = difference <= 0;

          if (isExpired && !string.IsNullOrWhiteSpace(refreshToken))
          {
            var result = await RefreshTokens(options, refreshToken, auth0Options.Backchannel);

            if (result != null)
            {
              context.Properties.UpdateTokenValue("access_token", result.AccessToken);
              context.Properties.UpdateTokenValue("refresh_token", result.RefreshToken);
              context.Properties.UpdateTokenValue("id_token", result.IdToken);
              context.Properties.UpdateTokenValue("expires_at", DateTimeOffset.Now.AddSeconds(result.ExpiresIn).ToString("o"));
            }
            else
            {
              context.Properties.UpdateTokenValue("refresh_token", null);
            }

            context.ShouldRenew = true;
          }
        } 
      }
    }
  })
  .AddOpenIdConnect("Auth0", options => {
    //...the code to configure OpenID Connect...
  });
}

你应该处理好所有关于确定令牌到期和请求新的访问令牌的细节。Auth0 ASP.NET Core认证SDK代表你做这些。你同意这很神奇吗?✨

Auth0组织支持

不仅ASP.NET Core Authentication SDK让你处理标准的OpenID Connect和OAuth职责,而且它还简化了你使用特定Auth0功能的生活,如Auth0组织。通过使用组织,你可以代表用户的逻辑组,如团队、合作伙伴、商业客户等。他们可以有特定的政策、品牌和其他定制。因此,为了使用户能够登录到一个组织,你需要在你的ASP.NET应用程序中指定它。下面是一个例子,说明你如何通过使用SDK来完成这个任务。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuth0WebAppAuthentication(options =>
  {
    options.Domain = Configuration["Auth0:Domain"];
    options.ClientId = Configuration["Auth0:ClientId"];
    options.Organization = Configuration["Auth0:Organization"];
  });
}

很直接,对吗?🙌 当然,你可以用组织做更多的事情。请看一下ASP.NET Core Authentication SDK文档以了解更多。

定制化

在这一点上,你可能会认为,拥有一个SDK,你就必须使用它的方法来与OpenID Connect和OAuth合作。因此,你不再有在授权请求中添加额外参数的自由,比如说。或者你可能认为你不能再应用你的自定义令牌验证或自由管理OpenID Connect事件。

我有一个好消息:你不需要担心这个问题。ASP.NET核心认证SDK让你可以自由地定制许多东西。

例如,你可以在授权请求中添加自定义参数。请看下面的例子。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuth0WebAppAuthentication(options =>
  {
    options.Domain = Configuration["Auth0:Domain"];
    options.ClientId = Configuration["Auth0:ClientId"];
    options.LoginParameters = new Dictionary<string, string>() { 
      { "screen_hint", "signup" } 
    };
  }); 
}

screen_hint 在这里,你将你的ASP.NET应用程序配置为在授权请求中附加带有signup 值的参数。在这种特定情况下,用户将看到注册窗口,而不是默认的登录屏幕。

但你也可以应用你的自定义验证,就像下面这个案例一样。

public void ConfigureServices(IServiceCollection services)
{
  services.AddAuth0WebAppAuthentication(options =>
  {
    options.Domain = Configuration["Auth0:Domain"];
    options.ClientId = Configuration["Auth0:ClientId"];
    options.OpenIdConnectEvents = new OpenIdConnectEvents
    {
      OnTokenValidated = (context) =>
      {
        var birthDateClaim = context.SecurityToken.Claims.SingleOrDefault(claim => claim.Type == "birthdate")?.Value;

        if (string.IsNullOrWhiteSpace(birthDateClaim))
        {
          context.Fail("The 'birthdate' claim must be present in the ID token.");
        }
            
        return Task.CompletedTask;
      }
    };
  })
}

正如你所看到的,ASP.NET核心认证SDK公开了OpenIdConnectEvents 选项,让你处理由库触发的标准事件。 Microsoft.AspNetCore.Authentication.OpenIdConnect库触发的标准事件。这样,你就可以把SDK的简单性和标准库的行为结合起来,获得最大的灵活性。

总结

现在你应该对使用ASP.NET Core Authentication SDK将Auth0集成到你的ASP.NET应用程序的好处有了更清楚的认识:更少的代码、更多的可读性、灵活性和最佳实践。因此,请尝试使用SDK,并按照QuickStart的要求将您的ASP.NET应用程序与Auth0集成。查看SDK文档以了解更多。