AspNet.Security.OpenIdConnect与OAuthAuthorizationProvider

时间:2018-08-03 14:57:50

标签: asp.net asp.net-core oauth-2.0 asp.net-core-2.0 openid-connect

我在.NET Framework中有一个应用程序,其中实现了OAuthAuthorizationServer。现在,我想将我的应用程序升级到.NET Core 2.1,因此我进行了一些研发,并决定使用ASOS。现在的问题是我已经实现了ASOS,并且工作正常,但是我有一些无法弄清楚如何转换的块。

private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    var identity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType),
        context.Scope.Select(x => new Claim("claim", x)));

    context.Validated(identity);

    return Task.FromResult(0);
}

private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context)
{
    var identity = new ClaimsIdentity(new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType),
        context.Scope.Select(x => new Claim("claim", x)));

    context.Validated(identity);

    return Task.FromResult(0);
}


private readonly ConcurrentDictionary<string, string> _authenticationCodes =
    new ConcurrentDictionary<string, string>(StringComparer.Ordinal);

private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
{
    context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
    _authenticationCodes[context.Token] = context.SerializeTicket();
}

private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
{
    string value;
    if (_authenticationCodes.TryRemove(context.Token, out value))
    {
        context.DeserializeTicket(value);
    }
}

private void CreateRefreshToken(AuthenticationTokenCreateContext context)
{
    context.SetToken(context.SerializeTicket());
}

private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
{
    context.DeserializeTicket(context.Token);
}

现在我有几个问题:

  • 客户端凭据和资源所有者密码授予类型是两种不同的授予类型,那么我们如何使用ASOS区分它们?
  • GrantResourceOwnerCredentialsOAuthGrantResourceOwnerCredentialsContext作为参数,GrantClientCredentialsOAuthGrantClientCredentialsContext作为参数。这两个上下文都包含在ASOS中不可用的范围。
  • 如何像执行OAuthAuthorizationProvider一样序列化和反序列化访问和刷新令牌?
  • 我们如何处理ASOS中的刷新令牌?我可以看到刷新令牌作为响应,但我自己没有为刷新令牌编写任何逻辑。

1 个答案:

答案 0 :(得分:3)

  

客户端凭据和资源所有者密码授予类型是两种不同的授予类型,那么我们如何使用ASOS区分它们?

public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
    if (context.Request.IsClientCredentialsGrantType())
    {
        // ...
    }

    else if (context.Request.IsPasswordGrantType())
    {
        // ...
    }

    else
    {
        throw new NotSupportedException();
    }
}
  

GrantResourceOwnerCredentials将OAuthGrantResourceOwnerCredentialsContext作为参数,GrantClientCredetails将OAuthGrantClientCredentialsContext作为参数。这两个上下文都包含在ASOS中不可用的范围

public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
    var scopes = context.Request.GetScopes();

    // ...
}
  

如何像执行OAuthAUthorizationProvider一样对访问和刷新令牌进行序列化和反序列化?

通过使用OnSerializeAccessToken / OnDeserializeAccessTokenOnSerializeRefreshToken / OnDeserializeRefreshToken事件。

  

我们如何处理ASOS中的刷新令牌?我可以看到刷新令牌作为响应,但是我自己没有为刷新令牌编写任何逻辑。

与Katana的OAuth服务器中间件不同,ASOS提供了用于生成授权码和刷新令牌的默认逻辑。如果您想使用诸如令牌吊销之类的实现工具,则可以在我提到的事件中做到这一点。阅读AspNet.Security.OpenIdConnect.Server. Refresh tokens了解更多信息。

下面是一个示例,该示例返回GUID刷新令牌并将关联的(加密的)有效负载存储在数据库中:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace AuthorizationServer
{
    public class MyToken
    {
        public string Id { get; set; }
        public string Payload { get; set; }
    }

    public class MyDbContext : DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options) { }

        public DbSet<MyToken> Tokens { get; set; }
    }

    public class MyProvider : OpenIdConnectServerProvider
    {
        private readonly MyDbContext _database;

        public MyProvider(MyDbContext database)
        {
            _database = database;
        }

        public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
        {
            if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType())
            {
                context.Reject(error: OpenIdConnectConstants.Errors.UnsupportedGrantType);
            }
            else
            {
                // Don't enforce client authentication.
                context.Skip();
            }
            return Task.CompletedTask;
        }

        public override async Task HandleTokenRequest(HandleTokenRequestContext context)
        {
            if (context.Request.IsPasswordGrantType())
            {
                if (context.Request.Username == "bob" && context.Request.Password == "bob")
                {
                    var identity = new ClaimsIdentity(context.Scheme.Name);
                    identity.AddClaim(new Claim(OpenIdConnectConstants.Claims.Subject, "Bob"));

                    var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), identity.AuthenticationType);
                    ticket.SetScopes(OpenIdConnectConstants.Scopes.OfflineAccess);

                    context.Validate(ticket);
                }
                else
                {
                    context.Reject(
                        error: OpenIdConnectConstants.Errors.InvalidGrant,
                        description: "The username/password couple is invalid.");
                }
            }
            else
            {
                var token = await _database.Tokens.FindAsync(context.Request.RefreshToken);
                _database.Tokens.Remove(token);
                await _database.SaveChangesAsync();

                context.Validate(context.Ticket);
            }
        }

        public override async Task SerializeRefreshToken(SerializeRefreshTokenContext context)
        {
            context.RefreshToken = Guid.NewGuid().ToString();
            _database.Tokens.Add(new MyToken
            {
                Id = context.RefreshToken,
                Payload = context.Options.RefreshTokenFormat.Protect(context.Ticket)
            });
            await _database.SaveChangesAsync();
        }

        public override async Task DeserializeRefreshToken(DeserializeRefreshTokenContext context)
        {
            context.HandleDeserialization();
            var token = await _database.Tokens.FindAsync(context.RefreshToken);
            if (token == null)
            {
                return;
            }

            context.Ticket = context.Options.RefreshTokenFormat.Unprotect(token.Payload);
        }
    }

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<MyDbContext>(options =>
            {
                options.UseInMemoryDatabase(nameof(MyDbContext));
            });

            services.AddAuthentication()
                .AddOpenIdConnectServer(options =>
                {
                    options.TokenEndpointPath = "/token";
                    options.ProviderType = typeof(MyProvider);
                    options.AllowInsecureHttp = true;
                })

                .AddOAuthValidation();

            services.AddMvc();

            services.AddScoped<MyProvider>();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseAuthentication();

            app.UseMvcWithDefaultRoute();
        }
    }
}