This repository was archived by the owner on Nov 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 68
/
Copy pathAuthenticationSessionUserTokenStore.cs
executable file
·134 lines (113 loc) · 5.73 KB
/
AuthenticationSessionUserTokenStore.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using IdentityModel;
namespace Duende.AccessTokenManagement.OpenIdConnect
{
/// <summary>
/// Token store using the ASP.NET Core authentication session
/// </summary>
public class AuthenticationSessionUserAccessTokenStore : IUserTokenStore
{
private readonly IHttpContextAccessor _contextAccessor;
private readonly IStoreTokensInAuthenticationProperties _tokensInProps;
private readonly ILogger<AuthenticationSessionUserAccessTokenStore> _logger;
/// <summary>
/// ctor
/// </summary>
/// <param name="contextAccessor"></param>
/// <param name="tokensInProps"></param>
/// <param name="logger"></param>
public AuthenticationSessionUserAccessTokenStore(
IHttpContextAccessor contextAccessor,
IStoreTokensInAuthenticationProperties tokensInProps,
ILogger<AuthenticationSessionUserAccessTokenStore> logger)
{
_contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
_logger = logger;
_tokensInProps = tokensInProps;
}
/// <inheritdoc/>
public async Task<UserToken> GetTokenAsync(
ClaimsPrincipal user,
UserTokenRequestParameters? parameters = null)
{
parameters ??= new();
// Resolve the cache here because it needs to have a per-request
// lifetime. Sometimes the store itself is captured for longer than
// that inside an HttpClient.
var cache = _contextAccessor.HttpContext?.RequestServices.GetRequiredService<AuthenticateResultCache>();
// check the cache in case the cookie was re-issued via StoreTokenAsync
// we use String.Empty as the key for a null SignInScheme
if (!cache!.TryGetValue(parameters.SignInScheme ?? String.Empty, out var result))
{
result = await _contextAccessor!.HttpContext!.AuthenticateAsync(parameters.SignInScheme).ConfigureAwait(false);
}
if (!result.Succeeded)
{
_logger.LogInformation("Cannot authenticate scheme: {scheme}", parameters.SignInScheme ?? "default signin scheme");
return new UserToken() { Error = "Cannot authenticate scheme" };
}
if (result.Properties == null)
{
_logger.LogInformation("Authentication result properties are null for scheme: {scheme}",
parameters.SignInScheme ?? "default signin scheme");
return new UserToken() { Error = "No properties on authentication result" };
}
return _tokensInProps.GetUserToken(result.Properties, parameters);
}
/// <inheritdoc/>
public async Task StoreTokenAsync(
ClaimsPrincipal user,
UserToken token,
UserTokenRequestParameters? parameters = null)
{
parameters ??= new();
// Resolve the cache here because it needs to have a per-request
// lifetime. Sometimes the store itself is captured for longer than
// that inside an HttpClient.
var cache = _contextAccessor.HttpContext?.RequestServices.GetRequiredService<AuthenticateResultCache>();
// check the cache in case the cookie was re-issued via StoreTokenAsync
// we use String.Empty as the key for a null SignInScheme
if (!cache!.TryGetValue(parameters.SignInScheme ?? String.Empty, out var result))
{
result = await _contextAccessor.HttpContext!.AuthenticateAsync(parameters.SignInScheme)!.ConfigureAwait(false);
}
if (result is not { Succeeded: true })
{
throw new Exception("Can't store tokens. User is anonymous");
}
// in case you want to filter certain claims before re-issuing the authentication session
var transformedPrincipal = await FilterPrincipalAsync(result.Principal!).ConfigureAwait(false);
_tokensInProps.SetUserToken(token, result.Properties, parameters);
var scheme = await _tokensInProps.GetSchemeAsync(parameters);
await _contextAccessor.HttpContext!.SignInAsync(scheme, transformedPrincipal, result.Properties).ConfigureAwait(false);
// add to the cache so if GetTokenAsync is called again, we will use the updated property values
// we use String.Empty as the key for a null SignInScheme
cache[parameters.SignInScheme ?? String.Empty] = AuthenticateResult.Success(new AuthenticationTicket(transformedPrincipal, result.Properties, scheme!));
}
/// <inheritdoc/>
public Task ClearTokenAsync(
ClaimsPrincipal user,
UserTokenRequestParameters? parameters = null)
{
// don't bother here, since likely we're in the middle of signing out
return Task.CompletedTask;
}
/// <summary>
/// Allows transforming the principal before re-issuing the authentication session
/// </summary>
/// <param name="principal"></param>
/// <returns></returns>
protected virtual Task<ClaimsPrincipal> FilterPrincipalAsync(ClaimsPrincipal principal)
{
return Task.FromResult(principal);
}
}
}