c#WebAPI Owin jwt RefreshToken invalid_grant

时间:2017-01-24 18:22:44

标签: c# asp.net asp.net-web-api owin jwt

我被这种行为困扰了。基本上,当请求刷新令牌时,WebAPI会执行从数据中删除旧的refresh_token的逻辑,并且我没有在调试器中看到任何错误。但是,API返回带有" invalid_grant"的HTTP 400。作为错误。

Startup.cs

// OAuth Server configuration
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
    AllowInsecureHttp = false,
    TokenEndpointPath = new PathString("/oauth2/token"),
    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(1),
    AccessTokenFormat = new AccessTokenJwtFormat(issuer),
    RefreshTokenProvider = new SimpleRefreshTokenProvider(),
    RefreshTokenFormat = new RefreshTokenJwtFormat(issuer),
    Provider = new CustomOAuthProvider()
};

// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);

SimpleRefreshTokenProvider.cs

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{

    public async Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var clientid = context.Ticket.Properties.Dictionary["as:client_id"];

        if (string.IsNullOrEmpty(clientid))
        {
            return;
        }

        var refreshTokenId = Guid.NewGuid().ToString("n");
        var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");

        var _repo = ClientDbProvider.GetInstance();
        var token = new RefreshToken()
        {
            Id = ClientHelper.GetHash(refreshTokenId),
            ClientId = clientid,
            Subject = context.Ticket.Identity.Name,
            IssuedUtc = DateTime.UtcNow,
            ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
        };

        context.Ticket.Properties.IssuedUtc = new DateTimeOffset(token.IssuedUtc);
        context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(token.ExpiresUtc);
        context.Ticket.Properties.Dictionary.Add("refreshTokenId", refreshTokenId);
        context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.Role, "refreshToken"));
        token.ProtectedTicket = context.SerializeTicket();
        var result = await _repo.AddRefreshToken(token);
        if (result)
        {
            context.SetToken(token.ProtectedTicket);
        }

        return;
    }

    public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {

        var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });

        var _repo = ClientDbProvider.GetInstance();
        string hashedTokenId = ClientHelper.GetHash(context.Token);
        var refreshToken = await _repo.FindRefreshToken(hashedTokenId);

        if (refreshToken != null)
        {
            //Get protectedTicket from refreshToken class
            context.DeserializeTicket(refreshToken.ProtectedTicket);
            var result = await _repo.RemoveRefreshToken(hashedTokenId);
        }
    }

    public void Create(AuthenticationTokenCreateContext context)
    {
        throw new NotImplementedException();
    }

    public void Receive(AuthenticationTokenReceiveContext context)
    {
        throw new NotImplementedException();
    }
}

RefreshTokenJwtFormat.cs

public class RefreshTokenJwtFormat : ISecureDataFormat<AuthenticationTicket>
    {
        private const string AudiencePropertyKey = "audience";

        private readonly string _issuer = string.Empty;

        public RefreshTokenJwtFormat(string issuer)
        {
            _issuer = issuer;
            audStore = new AudiencesStore();
        }

        public string Protect(AuthenticationTicket data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            string audienceId = data.Properties.Dictionary.ContainsKey(AudiencePropertyKey) ? data.Properties.Dictionary[AudiencePropertyKey] : null;

            if (string.IsNullOrWhiteSpace(audienceId)) throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");

            var audience = GetAudience(audienceId);
            string symmetricKeyAsBase64 = audience.Base64Secret;

            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
            var signingCredentials = new SigningCredentials(
                    new InMemorySymmetricSecurityKey(keyByteArray),
                    SecurityAlgorithms.HmacSha256Signature,
                    SecurityAlgorithms.Sha256Digest);

            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;
            var payload = new JwtPayload(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime);
            if (data.Properties.Dictionary.ContainsKey("refreshTokenId"))
            {
                payload.Add("refreshTokenId", data.Properties.Dictionary["refreshTokenId"]);
            }

            var header = new JwtHeader(signingCredentials);
            var token = new JwtSecurityToken(header, payload);

            var handler = new JwtSecurityTokenHandler();

            var jwt = handler.WriteToken(token);

            return jwt;
        }

        public AuthenticationTicket Unprotect(string protectedText)
        {
            var handler = new JwtSecurityTokenHandler();
            SecurityToken securityToken = handler.ReadToken(protectedText);
            var audienceId = ((JwtSecurityToken)securityToken).Claims.First(x => x.Type == "aud").Value;
            var audience = GetAudience(audienceId);

            string symmetricKeyAsBase64 = audience.Base64Secret;
            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
            var securityKey = new InMemorySymmetricSecurityKey(keyByteArray);

            var validationParameters = new TokenValidationParameters()
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = securityKey,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero,
                ValidateAudience = true,
                ValidAudience = audienceId,
                ValidateIssuer = true,
                ValidIssuer = _issuer
            };

            SecurityToken validatedToken;
            ClaimsPrincipal principal = null;
            try
            {
                principal = handler.ValidateToken(protectedText, validationParameters, out validatedToken);
            }
            catch(Exception ex)
            {
                return null;
            }

            return new AuthenticationTicket(principal.Identities.First(), new AuthenticationProperties());
        }

        private Models.Audience GetAudience(string audienceId)
        {
            var findAudTask = Task.Run(() => audStore.FindAudienceAsync(audienceId));
            findAudTask.Wait();
            var audience = findAudTask.Result;
            return audience;
        }

        private AudiencesStore audStore;
    }

2 个答案:

答案 0 :(得分:2)

首先你需要 在CustomOAuthProvider中实现GrantRefreshToken(OAuthGrantRefreshTokenContext上下文)

之类的东西
return new AuthenticationTicket(principal.Identities.First(), new AuthenticationProperties());

其次,您需要在RefreshTokenJwtFormat中更改方法Unprotect。

替换

return new AuthenticationTicket(principal.Identities.First(), new AuthenticationProperties
                {
                    IssuedUtc = validatedToken.ValidFrom,
                    ExpiresUtc = validatedToken.ValidTo
                });

import java.math.BigDecimal;
import java.util.Scanner;

public class LargestOfTwo {

    private Scanner scanner;
    private Number a;
    private Number b;

    public static void main(String args[]) throws Exception {
        LargestOfTwo app = new LargestOfTwo();
        app.start();
    }

    private void start() {
        readInputs();
        Number largest = compare(a, b);
        System.out.print("Largest of the numbers is: " + largest);
    }

    private void readInputs() {
        scanner = new Scanner(System.in);
        System.out.println("Enter two numbers, and I will show you which one is largest\n");
        a = readInput();
        b = readInput();
    }

    private Number readInput() {
        Double d = scanner.nextDouble();
        if (d == Math.floor(d)) {
            return d.intValue();
        } else {
            return d;
        }
    }

    private Number compare(Number x, Number y) {
        if (new BigDecimal(x.floatValue()).compareTo(new BigDecimal(y.floatValue())) > 0) {
            return x;
        } else {
            return y;
        }
    }
}

答案 1 :(得分:1)

我有类似的(工作)代码。我不是这方面的专家,但我对我的代码进行了一些比较并注意到:

在SimpleRefreshToken.cs中:

在receiveAsync中,我的代码以:

结尾
$(document).ready(function( e ){
    //Add an preventDefault on your event in order to not do the submit of the form.
    e.preventDefault(); 
    $("button").click(function(){
        //Get all the data from your form
        var data = $(form).serialize();
        $.ajax({
            type: 'POST',
            //send the data with the ajax.
            // You can retrieve it in your php through $_POST['name of form field']
            data: data,
            url: 'calcc.php',
            success: function(data) {
                $("p").text(data);
            }
        });
    });
 });

设置故障单并返回值。

对于createAsync,我的代码以:

结束
context.SetTicket(context.Ticket);   
    return Task.FromResult<object>(null);

尽管如此,不知道这是否会有所帮助