Как настроить ошибку авторизации, созданную OpenIddict? - PullRequest
0 голосов
/ 12 ноября 2018

Я использую OpenIddict для аутентификации в .NET Core 2 API.Клиентская сторона Я полагаюсь на любые ошибки API, чтобы следовать пользовательской схеме.Однако, когда, например, токен обновления устарел, я не могу понять, как настроить отправленную ошибку.

Конечная точка / token никогда не достигается, поэтому ошибка не находится под моим контролем.

Результатом запроса является код состояния 400 со следующим JSON:

{"error":"invalid_grant","error_description":"The specified refresh token is no longer valid."}

Я пытался использовать пользовательское промежуточное ПО для перехвата всех кодов состояния (что он и делает), но результат возвращается до выполнения моего пользовательского промежуточного программного обеспечения.

Как правильно настроить ошибку или перехватить ее, чтобы изменить?Спасибо!

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Вы можете использовать модель событий OpenIddict для настройки полезных нагрузок ответа токена перед их записью в поток ответа. Вот пример:

MyApplyTokenResponseHandler.cs

public class MyApplyTokenResponseHandler : IOpenIddictServerEventHandler<OpenIddictServerEvents.ApplyTokenResponse>
{
    public Task<OpenIddictServerEventState> HandleAsync(OpenIddictServerEvents.ApplyTokenResponse notification)
    {
        var response = notification.Context.Response;
        if (string.Equals(response.Error, OpenIddictConstants.Errors.InvalidGrant, StringComparison.Ordinal) &&
           !string.IsNullOrEmpty(response.ErrorDescription))
        {
            response.ErrorDescription = "Your customized error";
        }

        return Task.FromResult(OpenIddictServerEventState.Unhandled);
    }
}

Startup.cs

services.AddOpenIddict()
    .AddCore(options =>
    {
        // ...
    })

    .AddServer(options =>
    {
        // ...
        options.AddEventHandler<MyApplyTokenResponseHandler>();
    })

    .AddValidation();
0 голосов
/ 13 ноября 2018

Конечная точка / токен никогда не достигается, поэтому ошибка не находится под моим контролем.

Фактически достигается /token, а параметр grant_type равен refresh_token. Но логика отклонения по истечении срока действия маркера обновления не обрабатывается нами. Это какой-то «жесткий код» в исходном коде :

if (token == null)
{
    context.Reject(
        error: OpenIddictConstants.Errors.InvalidGrant,
        description: context.Request.IsAuthorizationCodeGrantType() ?
            "The specified authorization code is no longer valid." :
            "The specified refresh token is no longer valid.");

    return;
}

if (options.UseRollingTokens || context.Request.IsAuthorizationCodeGrantType())
{
    if (!await TryRedeemTokenAsync(token))
    {
        context.Reject(
            error: OpenIddictConstants.Errors.InvalidGrant,
            description: context.Request.IsAuthorizationCodeGrantType() ?
                "The specified authorization code is no longer valid." :
                "The specified refresh token is no longer valid.");

        return;
    }
}

context.Reject здесь происходит от сборки AspNet.Security.OpenIdConnect.Server.

Подробнее см. исходный код на GitHub .

Я пытался использовать пользовательское промежуточное ПО для перехвата всех кодов состояния (что оно и делает), но результат возвращается до завершения выполнения моего пользовательского промежуточного ПО.

Я пытался, и я почти уверен, что мы можем использовать специальное промежуточное ПО для перехвата всех кодов состояния. Ключевым моментом является обнаружение кода состояния после вызова next():

app.Use(async(context , next )=>{

    // passby all other end points
    if(! context.Request.Path.StartsWithSegments("/connect/token")){
        await next();
        return;
    }

    // since we might want to detect the Response.Body, I add some stream here .
    // if you only want to detect the status code , there's no need to use these streams
    Stream originalStream = context.Response.Body;
    var hijackedStream = new MemoryStream();
    context.Response.Body = hijackedStream;
    hijackedStream.Seek(0,SeekOrigin.Begin);

    await next();

    // if status code not 400 , pass by
    if(context.Response.StatusCode != 400){
        await CopyStreamToResponseBody(context,hijackedStream,originalStream);
        return;
    }

    // read and custom the stream 
    hijackedStream.Seek(0,SeekOrigin.Begin);
    using (StreamReader sr = new StreamReader(hijackedStream))
    {
        var raw= sr.ReadToEnd();
        if(raw.Contains("The specified refresh token is no longer valid.")){
            // custom your own response
            context.Response.StatusCode = 401;
            // ...
            //context.Response.Body = ... /
        }else{
            await CopyStreamToResponseBody(context,hijackedStream,originalStream);
        }
    }
});

// helper to make the copy easy
private async Task CopyStreamToResponseBody(HttpContext context,Stream newStream, Stream originalStream){

    newStream.Seek(0,SeekOrigin.Begin);
    await newStream.CopyToAsync(originalStream);
    context.Response.ContentLength =originalStream.Length;
    context.Response.Body = originalStream;
}
...