-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathBackchannelLogoutHandler.cs
More file actions
138 lines (119 loc) · 5.52 KB
/
BackchannelLogoutHandler.cs
File metadata and controls
138 lines (119 loc) · 5.52 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
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
namespace Auth0.AspNetCore.Authentication.BackchannelLogout
{
public class BackchannelLogoutHandler
{
private readonly ILogoutTokenHandler _tokenHandler;
private readonly string _authenticationScheme;
public BackchannelLogoutHandler(ILogoutTokenHandler tokenHandler)
: this(tokenHandler, Auth0Constants.AuthenticationScheme)
{
}
public BackchannelLogoutHandler(ILogoutTokenHandler tokenHandler, string authenticationScheme)
{
_tokenHandler = tokenHandler;
_authenticationScheme = authenticationScheme;
}
public async Task HandleRequestAsync(HttpContext context)
{
try
{
context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
if (context.Request.HasFormContentType)
{
var logoutToken = context.Request.Form["logout_token"].FirstOrDefault();
if (!String.IsNullOrWhiteSpace(logoutToken))
{
var auth0Options = context.RequestServices
.GetRequiredService<IOptionsSnapshot<Auth0WebAppOptions>>()
.Get(_authenticationScheme);
var oidcOptions = context.RequestServices
.GetRequiredService<IOptionsSnapshot<OpenIdConnectOptions>>()
.Get(_authenticationScheme);
var principal = await ValidateLogoutToken(logoutToken, oidcOptions, context);
if (principal != null)
{
var issuer = principal.Claims.First(c => c.Type == "iss").Value;
var sid = principal.Claims.First(c => c.Type == "sid").Value;
var cookieOptions = context.RequestServices
.GetRequiredService<IOptionsSnapshot<CookieAuthenticationOptions>>()
.Get(auth0Options.CookieAuthenticationScheme);
await _tokenHandler.OnTokenReceivedAsync(issuer, sid, logoutToken,
cookieOptions.ExpireTimeSpan);
}
}
else
{
await context.WriteErrorAsync(400, "invalid_request", "Missing logout_token.");
}
}
else
{
await context.WriteErrorAsync(400, "invalid_request",
"Only application/x-www-form-urlencoded is allowed.");
}
}
catch (LogoutTokenValidationException ex)
{
await context.WriteErrorAsync(400, "invalid_request", ex.Message);
}
catch (Exception ex)
{
await context.WriteErrorAsync(400, "invalid_request", ex.Message);
}
}
private async Task<ClaimsPrincipal> ValidateLogoutToken(String token, OpenIdConnectOptions oidcOptions, HttpContext context)
{
OpenIdConnectConfiguration? configuration = null;
if (oidcOptions.ConfigurationManager != null)
{
configuration = await oidcOptions.ConfigurationManager.GetConfigurationAsync(context.RequestAborted);
}
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
RequireExpirationTime = true,
ValidIssuer = oidcOptions.TokenValidationParameters.ValidIssuer,
ValidAudience = oidcOptions.TokenValidationParameters.ValidAudience,
};
if (configuration != null)
{
tokenValidationParameters.IssuerSigningKeys =
oidcOptions.TokenValidationParameters.IssuerSigningKeys?.Concat(configuration.SigningKeys)
?? configuration.SigningKeys;
}
var principal =
oidcOptions.SecurityTokenValidator.ValidateToken(token, tokenValidationParameters, out SecurityToken _);
LogoutTokenValidator.Validate(new JwtSecurityTokenHandler().ReadJwtToken(token));
return principal;
}
}
public static class HttpContextExtensions
{
public static async Task WriteErrorAsync(this HttpContext context, int statusCode, string error, string description)
{
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json";
var json = System.Text.Json.JsonSerializer.Serialize(new { error, error_description = description });
await context.Response.WriteAsync(json);
}
public static async Task WriteStatusCodeAsync(this HttpContext context, int statusCode)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(string.Empty);
}
}
}