-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOpenIdService.cs
More file actions
210 lines (187 loc) · 8.48 KB
/
OpenIdService.cs
File metadata and controls
210 lines (187 loc) · 8.48 KB
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Web;
using IdentityModel.Client;
namespace OpenId.AspNet.Authentication
{
internal class OpenIdService : IOpenIdService
{
internal readonly OpenIdConnectOptions Options;
private readonly ConcurrentDictionary<string, DiscoveryResponse> _discoveryResposeMap = new ConcurrentDictionary<string, DiscoveryResponse>();
internal OpenIdService(OpenIdConnectOptions options)
{
this.Options = options;
}
private DiscoveryClient CreateDiscoveryClient(string authorityUrl)
{
var policy = new DiscoveryPolicy {
RequireKeySet = true,
RequireHttps = Options.RequireHttps,
ValidateIssuerName = true,
ValidateEndpoints = true,
};
var client = new DiscoveryClient(authorityUrl);
client.Policy = policy;
return client;
}
internal async Task<Tuple<DiscoveryResponse, int, string>> GetDiscoveryResponseAsync(string authorityUrl)
{
authorityUrl = authorityUrl ?? Options.Authority;
authorityUrl = authorityUrl.ToLowerInvariant().RemoveTrailingSlash();
DiscoveryResponse result;
if(_discoveryResposeMap.TryGetValue(authorityUrl, out result))
{
return new Tuple<DiscoveryResponse, int, string>(result, 0, null);
}
result = null;
var client = CreateDiscoveryClient(authorityUrl);
var idsInfo = await client.GetAsync().ConfigureAwait(false);
if(idsInfo.ErrorType == ResponseErrorType.None)
{
_discoveryResposeMap[authorityUrl] = idsInfo;
result = idsInfo;
}
return new Tuple<DiscoveryResponse, int, string>(result, (int)idsInfo.ErrorType, idsInfo.Error);
}
/// <summary>
/// Gets the discovery information for specified Identity Server (Authority).
/// </summary>
/// <param name="authorityUrl">Optional. The authority URL.
/// If not specified, then default Authority will be used <see cref="OpenIdConnectOptions"/>
/// </param>
/// <returns>
/// Discover Response
/// </returns>
/// <exception cref="System.InvalidOperationException"></exception>
public DiscoveryResponse GetDiscoveryInfo(string authorityUrl = null)
{
var result = GetDiscoveryInfoAsyc(authorityUrl).GetAwaiter().GetResult();
return result;
}
/// <summary>
/// Gets the discovery information for specified Identity Server (Authority).
/// </summary>
/// <param name="authorityUrl">Optional. The authority URL.
/// If not specified, then default Authority will be used <see cref="OpenIdConnectOptions"/>
/// </param>
/// <returns>
/// Discover Response
/// </returns>
/// <exception cref="System.InvalidOperationException"></exception>
public async Task<DiscoveryResponse> GetDiscoveryInfoAsyc(string authorityUrl = null)
{
authorityUrl = authorityUrl ?? Options.Authority;
var tuple = await GetDiscoveryResponseAsync(authorityUrl).ConfigureAwait(false);
if(tuple.Item1 == null)
{
throw new InvalidOperationException($"Identity Server '{authorityUrl}' Discovery endpoint communication has failed.");
}
return tuple.Item1;
}
/// <summary>
/// Gets the access token on behalf of currently logged user.
/// </summary>
/// <returns></returns>
public string GetUserAccessToken()
{
return GetUserAccessTokenAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Gets the access token on behalf of currently logged user.
/// </summary>
/// <returns></returns>
public async Task<string> GetUserAccessTokenAsync()
{
//get OpenId Authorization Data from the http context
var httpContext = HttpContext.Current;
var opts = Options;
var openIdData = httpContext.Items[Const.AuthorizationDataKey] as AuthorizationData;
if(openIdData == null)
{
return null;
}
if(openIdData.AccessToken != null)
{
return openIdData.AccessToken;
}
if(openIdData.Code == null)
{
return null;
}
//get discovery info
var tuple = await GetDiscoveryResponseAsync(opts.Authority).ConfigureAwait(false);
var disco = tuple.Item1;
if(disco == null)
{
return null;
}
TokenResponse tokenResponse;
var expirationTime = DateTime.Now;
using(var tokenHttpClient = new TokenClient(
address: disco.TokenEndpoint,
clientId: opts.ClientId,
clientSecret: opts.ClientSecret,
style: AuthenticationStyle.PostValues
))
{
var uri = httpContext.Request.Url;
var redirectUri = httpContext.BaseUrl() + opts.Authentication.SignInEndpoint;
tokenResponse = await tokenHttpClient.RequestAuthorizationCodeAsync(openIdData.Code, redirectUri).ConfigureAwait(false);
}
if(tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
openIdData.AccessToken = tokenResponse.AccessToken;
openIdData.RefreshToken = tokenResponse.RefreshToken;
openIdData.AccessTokenExpirationTime = expirationTime.AddSeconds(tokenResponse.ExpiresIn);
//erase code, it cannot be reused anyway
openIdData.Code = null;
return tokenResponse.AccessToken;
}
/// <summary>
/// Gets the Client/Application security token. That can be used to access OAuth2 resources
/// </summary>
/// <param name="resourceId">The resource identifier for which credentials are being requested</param>
/// <param name="clientId">The Client identifier. Default: <see cref="OpenIdConnectOptions.ClientId" /></param>
/// <param name="clientSecret">The client secret. Default: <see cref="OpenIdConnectOptions.ClientSecret" /></param>
/// <param name="authorityUrl">The authority URL. <see cref="OpenIdConnectOptions.Authority" /></param>
/// <returns>
/// Encoded JWT token
/// </returns>
public string GetClientCredentials(string resourceId, string clientId = null, string clientSecret = null, string authorityUrl = null)
{
return GetClientCredentialsAsync(resourceId, clientId, clientSecret, authorityUrl).GetAwaiter().GetResult();
}
/// <summary>
/// Gets the client credentials asynchronous.
/// </summary>
/// <param name="resourceId">The resource identifier.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="clientSecret">The client secret.</param>
/// <param name="authorityUrl">The authority URL.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">resourceId</exception>
/// <exception cref="System.InvalidOperationException"></exception>
public async Task<string> GetClientCredentialsAsync(string resourceId, string clientId = null, string clientSecret = null, string authorityUrl = null)
{
if(string.IsNullOrWhiteSpace(resourceId))
{
throw new ArgumentNullException(nameof(resourceId));
}
clientId = clientId ?? Options.ClientId;
clientSecret = clientSecret ?? Options.ClientSecret;
authorityUrl = authorityUrl ?? Options.Authority;
//get Discover info
var disco = await GetDiscoveryInfoAsyc(authorityUrl).ConfigureAwait(false);
var tokenClient = new TokenClient(disco.TokenEndpoint, clientId, clientSecret);
var tokenResponse = await tokenClient.RequestClientCredentialsAsync(resourceId).ConfigureAwait(false);
if(tokenResponse.IsError)
{
throw new InvalidOperationException(tokenResponse.Error);
}
return tokenResponse.AccessToken;
}
}
}