Skip to content
This repository was archived by the owner on Dec 24, 2020. It is now read-only.

Commit d4cb6c2

Browse files
committed
Introduce a new DecryptToken event in the validation handler
1 parent 17bce14 commit d4cb6c2

File tree

14 files changed

+363
-67
lines changed

14 files changed

+363
-67
lines changed

src/AspNet.Security.OAuth.Introspection/OAuthIntrospectionEvents.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,27 @@ public class OAuthIntrospectionEvents
1717
/// <summary>
1818
/// Invoked when a challenge response is returned to the caller.
1919
/// </summary>
20-
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.FromResult(0);
20+
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.CompletedTask;
2121

2222
/// <summary>
2323
/// Invoked when a ticket is to be created from an introspection response.
2424
/// </summary>
25-
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.FromResult(0);
25+
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.CompletedTask;
2626

2727
/// <summary>
2828
/// Invoked when a token is to be sent to the authorization server for introspection.
2929
/// </summary>
30-
public Func<SendIntrospectionRequestContext, Task> OnSendIntrospectionRequest { get; set; } = context => Task.FromResult(0);
30+
public Func<SendIntrospectionRequestContext, Task> OnSendIntrospectionRequest { get; set; } = context => Task.CompletedTask;
3131

3232
/// <summary>
3333
/// Invoked when a token is to be parsed from a newly-received request.
3434
/// </summary>
35-
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.FromResult(0);
35+
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.CompletedTask;
3636

3737
/// <summary>
3838
/// Invoked when a token is to be validated, before final processing.
3939
/// </summary>
40-
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.FromResult(0);
40+
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.CompletedTask;
4141

4242
/// <summary>
4343
/// Invoked when a challenge response is returned to the caller.

src/AspNet.Security.OAuth.Introspection/OAuthIntrospectionHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ private Task StoreTicketAsync(string token, AuthenticationTicket ticket)
806806
{
807807
if (Options.CachingPolicy == null)
808808
{
809-
return Task.FromResult(0);
809+
return Task.CompletedTask;
810810
}
811811

812812
var bytes = Encoding.UTF8.GetBytes(Options.AccessTokenFormat.Protect(ticket));
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Extensions for more information
4+
* concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using JetBrains.Annotations;
8+
using Microsoft.AspNetCore.Authentication;
9+
using Microsoft.AspNetCore.Http;
10+
11+
namespace AspNet.Security.OAuth.Validation
12+
{
13+
/// <summary>
14+
/// Allows custom decryption of access tokens.
15+
/// </summary>
16+
public class DecryptTokenContext : ResultContext<OAuthValidationOptions>
17+
{
18+
public DecryptTokenContext(
19+
[NotNull] HttpContext context,
20+
[NotNull] AuthenticationScheme scheme,
21+
[NotNull] OAuthValidationOptions options,
22+
[NotNull] string token)
23+
: base(context, scheme, options)
24+
{
25+
Token = token;
26+
}
27+
28+
/// <summary>
29+
/// Gets the access token.
30+
/// </summary>
31+
public string Token { get; }
32+
33+
/// <summary>
34+
/// Gets or sets the data format used to deserialize the authentication ticket.
35+
/// </summary>
36+
public ISecureDataFormat<AuthenticationTicket> DataFormat { get; set; }
37+
}
38+
}

src/AspNet.Security.OAuth.Validation/OAuthValidationEvents.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,27 @@ public class OAuthValidationEvents
1717
/// <summary>
1818
/// Invoked when a challenge response is returned to the caller.
1919
/// </summary>
20-
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.FromResult(0);
20+
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.CompletedTask;
2121

2222
/// <summary>
2323
/// Invoked when a ticket is to be created from an introspection response.
2424
/// </summary>
25-
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.FromResult(0);
25+
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.CompletedTask;
26+
27+
/// <summary>
28+
/// Invoked when a token is to be decrypted.
29+
/// </summary>
30+
public Func<DecryptTokenContext, Task> OnDecryptToken { get; set; } = context => Task.CompletedTask;
2631

2732
/// <summary>
2833
/// Invoked when a token is to be parsed from a newly-received request.
2934
/// </summary>
30-
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.FromResult(0);
35+
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.CompletedTask;
3136

3237
/// <summary>
3338
/// Invoked when a token is to be validated, before final processing.
3439
/// </summary>
35-
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.FromResult(0);
40+
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.CompletedTask;
3641

3742
/// <summary>
3843
/// Invoked when a challenge response is returned to the caller.
@@ -44,6 +49,11 @@ public class OAuthValidationEvents
4449
/// </summary>
4550
public virtual Task CreateTicket(CreateTicketContext context) => OnCreateTicket(context);
4651

52+
/// <summary>
53+
/// Invoked when a token is to be decrypted.
54+
/// </summary>
55+
public virtual Task DecryptToken(DecryptTokenContext context) => OnDecryptToken(context);
56+
4757
/// <summary>
4858
/// Invoked when a token is to be parsed from a newly-received request.
4959
/// </summary>

src/AspNet.Security.OAuth.Validation/OAuthValidationHandler.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,45 @@ private bool ValidateAudience(AuthenticationTicket ticket)
348348
return false;
349349
}
350350

351+
private async Task<AuthenticateResult> DecryptTokenAsync(string token)
352+
{
353+
var notification = new DecryptTokenContext(Context, Scheme, Options, token)
354+
{
355+
DataFormat = Options.AccessTokenFormat
356+
};
357+
358+
await Events.DecryptToken(notification);
359+
360+
if (notification.Result != null)
361+
{
362+
Logger.LogInformation("The default authentication handling was skipped from user code.");
363+
364+
return notification.Result;
365+
}
366+
367+
if (notification.DataFormat == null)
368+
{
369+
throw new InvalidOperationException("A data formatter must be provided.");
370+
}
371+
372+
var ticket = notification.DataFormat.Unprotect(token);
373+
if (ticket == null)
374+
{
375+
return AuthenticateResult.Fail("Authentication failed because the access token was invalid.");
376+
}
377+
378+
return AuthenticateResult.Success(ticket);
379+
}
380+
351381
private async Task<AuthenticateResult> CreateTicketAsync(string token)
352382
{
353-
var ticket = Options.AccessTokenFormat.Unprotect(token);
383+
var result = await DecryptTokenAsync(token);
384+
if (!result.Succeeded)
385+
{
386+
return result;
387+
}
388+
389+
var ticket = result.Ticket;
354390
if (ticket == null)
355391
{
356392
return AuthenticateResult.Fail("Authentication failed because the access token was invalid.");

src/Owin.Security.OAuth.Introspection/OAuthIntrospectionEvents.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,27 @@ public class OAuthIntrospectionEvents
1717
/// <summary>
1818
/// Invoked when a challenge response is returned to the caller.
1919
/// </summary>
20-
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.FromResult(0);
20+
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.CompletedTask;
2121

2222
/// <summary>
2323
/// Invoked when a ticket is to be created from an introspection response.
2424
/// </summary>
25-
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.FromResult(0);
25+
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.CompletedTask;
2626

2727
/// <summary>
2828
/// Invoked when a token is to be parsed from a newly-received request.
2929
/// </summary>
30-
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.FromResult(0);
30+
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.CompletedTask;
3131

3232
/// <summary>
3333
/// Invoked when a token is to be sent to the authorization server for introspection.
3434
/// </summary>
35-
public Func<SendIntrospectionRequestContext, Task> OnSendIntrospectionRequest { get; set; } = context => Task.FromResult(0);
35+
public Func<SendIntrospectionRequestContext, Task> OnSendIntrospectionRequest { get; set; } = context => Task.CompletedTask;
3636

3737
/// <summary>
3838
/// Invoked when a token is to be validated, before final processing.
3939
/// </summary>
40-
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.FromResult(0);
40+
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.CompletedTask;
4141

4242
/// <summary>
4343
/// Invoked when a challenge response is returned to the caller.

src/Owin.Security.OAuth.Introspection/OAuthIntrospectionHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ private Task StoreTicketAsync(string token, AuthenticationTicket ticket)
749749
{
750750
if (Options.CachingPolicy == null)
751751
{
752-
return Task.FromResult(0);
752+
return Task.CompletedTask;
753753
}
754754

755755
var bytes = Encoding.UTF8.GetBytes(Options.AccessTokenFormat.Protect(ticket));
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Extensions for more information
4+
* concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using JetBrains.Annotations;
8+
using Microsoft.Owin;
9+
using Microsoft.Owin.Security;
10+
using Microsoft.Owin.Security.Provider;
11+
12+
namespace Owin.Security.OAuth.Validation
13+
{
14+
/// <summary>
15+
/// Allows custom decryption of access tokens.
16+
/// </summary>
17+
public class DecryptTokenContext : BaseContext<OAuthValidationOptions>
18+
{
19+
public DecryptTokenContext(
20+
[NotNull] IOwinContext context,
21+
[NotNull] OAuthValidationOptions options,
22+
[NotNull] string token)
23+
: base(context, options)
24+
{
25+
Token = token;
26+
}
27+
28+
/// <summary>
29+
/// Gets the access token.
30+
/// </summary>
31+
public string Token { get; }
32+
33+
/// <summary>
34+
/// Gets or sets the data format used to deserialize the authentication ticket.
35+
/// </summary>
36+
public ISecureDataFormat<AuthenticationTicket> DataFormat { get; set; }
37+
38+
/// <summary>
39+
/// Gets or sets the <see cref="AuthenticationTicket"/>
40+
/// extracted from the access token.
41+
/// </summary>
42+
public AuthenticationTicket Ticket { get; set; }
43+
44+
/// <summary>
45+
/// Gets a boolean indicating if the operation was handled from user code.
46+
/// </summary>
47+
public bool Handled { get; private set; }
48+
49+
/// <summary>
50+
/// Marks the operation as handled to prevent the default logic from being applied.
51+
/// </summary>
52+
public void HandleDecryption() => Handled = true;
53+
54+
/// <summary>
55+
/// Marks the operation as handled to prevent the default logic from being applied.
56+
/// </summary>
57+
/// <param name="ticket">The authentication ticket to use.</param>
58+
public void HandleDecryption(AuthenticationTicket ticket)
59+
{
60+
Ticket = ticket;
61+
Handled = true;
62+
}
63+
}
64+
}

src/Owin.Security.OAuth.Validation/OAuthValidationEvents.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,27 @@ public class OAuthValidationEvents
1717
/// <summary>
1818
/// Invoked when a challenge response is returned to the caller.
1919
/// </summary>
20-
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.FromResult(0);
20+
public Func<ApplyChallengeContext, Task> OnApplyChallenge { get; set; } = context => Task.CompletedTask;
2121

2222
/// <summary>
2323
/// Invoked when a ticket is to be created from an access token.
2424
/// </summary>
25-
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.FromResult(0);
25+
public Func<CreateTicketContext, Task> OnCreateTicket { get; set; } = context => Task.CompletedTask;
26+
27+
/// <summary>
28+
/// Invoked when a token is to be decrypted.
29+
/// </summary>
30+
public Func<DecryptTokenContext, Task> OnDecryptToken { get; set; } = context => Task.CompletedTask;
2631

2732
/// <summary>
2833
/// Invoked when a token is to be parsed from a newly-received request.
2934
/// </summary>
30-
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.FromResult(0);
35+
public Func<RetrieveTokenContext, Task> OnRetrieveToken { get; set; } = context => Task.CompletedTask;
3136

3237
/// <summary>
3338
/// Invoked when a token is to be validated, before final processing.
3439
/// </summary>
35-
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.FromResult(0);
40+
public Func<ValidateTokenContext, Task> OnValidateToken { get; set; } = context => Task.CompletedTask;
3641

3742
/// <summary>
3843
/// Invoked when a challenge response is returned to the caller.
@@ -44,6 +49,11 @@ public class OAuthValidationEvents
4449
/// </summary>
4550
public virtual Task CreateTicket(CreateTicketContext context) => OnCreateTicket(context);
4651

52+
/// <summary>
53+
/// Invoked when a token is to be decrypted.
54+
/// </summary>
55+
public virtual Task DecryptToken(DecryptTokenContext context) => OnDecryptToken(context);
56+
4757
/// <summary>
4858
/// Invoked when a token is to be parsed from a newly-received request.
4959
/// </summary>

src/Owin.Security.OAuth.Validation/OAuthValidationHandler.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,33 @@ private bool ValidateAudience(AuthenticationTicket ticket)
324324
return false;
325325
}
326326

327+
private async Task<AuthenticationTicket> DecryptTokenAsync(string token)
328+
{
329+
var notification = new DecryptTokenContext(Context, Options, token)
330+
{
331+
DataFormat = Options.AccessTokenFormat
332+
};
333+
334+
await Options.Events.DecryptToken(notification);
335+
336+
if (notification.Handled)
337+
{
338+
Logger.LogInformation("The default authentication handling was skipped from user code.");
339+
340+
return notification.Ticket;
341+
}
342+
343+
if (notification.DataFormat == null)
344+
{
345+
throw new InvalidOperationException("A data formatter must be provided.");
346+
}
347+
348+
return notification.DataFormat.Unprotect(token);
349+
}
350+
327351
private async Task<AuthenticationTicket> CreateTicketAsync(string token)
328352
{
329-
var ticket = Options.AccessTokenFormat.Unprotect(token);
353+
var ticket = await DecryptTokenAsync(token);
330354
if (ticket == null)
331355
{
332356
return null;

0 commit comments

Comments
 (0)