Skip to content
This repository was archived by the owner on Aug 4, 2024. It is now read-only.

Commit cdfbffe

Browse files
committed
Adds support for logging out when using an external authentication provider
1 parent 0e4c9d7 commit cdfbffe

File tree

11 files changed

+105
-36
lines changed

11 files changed

+105
-36
lines changed

src/Lithnet.Laps.Web/Lithnet.Laps.Web/App_LocalResources/UIMessages.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Lithnet.Laps.Web/Lithnet.Laps.Web/App_LocalResources/UIMessages.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,7 @@
165165
<data name="RateLimitError" xml:space="preserve">
166166
<value>You have exceeded the maximum number of requests allowed in a specified period of time</value>
167167
</data>
168+
<data name="LoggedOut" xml:space="preserve">
169+
<value>You have been logged out. Please close all browser windows.</value>
170+
</data>
168171
</root>

src/Lithnet.Laps.Web/Lithnet.Laps.Web/Content/Site.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ body {
2525
text-align: center;
2626
}
2727

28+
.center-content {
29+
align-items: center;
30+
text-align: center;
31+
}
32+
2833
.app-logo {
2934
max-width: 200px;
3035
}
@@ -253,3 +258,7 @@ body {
253258
margin-top: 0;
254259
}
255260
}
261+
262+
.logout-link {
263+
color: #777;
264+
}

src/Lithnet.Laps.Web/Lithnet.Laps.Web/Controllers/HomeController.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,24 @@ public ActionResult AuthNError()
1818
{
1919
return this.View();
2020
}
21+
22+
public ActionResult SignOut()
23+
{
24+
if (this.Request.GetOwinContext().Authentication.User.Identity.IsAuthenticated)
25+
{
26+
this.Request.GetOwinContext()
27+
.Authentication
28+
.SignOut(this.HttpContext.GetOwinContext()
29+
.Authentication.GetAuthenticationTypes()
30+
.Select(o => o.AuthenticationType).ToArray());
31+
}
32+
33+
return this.View("LogOut");
34+
}
35+
36+
public ActionResult LogOut()
37+
{
38+
return this.View();
39+
}
2140
}
2241
}

src/Lithnet.Laps.Web/Lithnet.Laps.Web/Global.asax.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,7 @@ protected void Application_Start()
1919
RouteConfig.RegisterRoutes(RouteTable.Routes);
2020
BundleConfig.RegisterBundles(BundleTable.Bundles);
2121
}
22+
23+
public static bool CanLogout => Startup.CanLogout && (HttpContext.Current?.User?.Identity?.IsAuthenticated ?? false);
2224
}
2325
}

src/Lithnet.Laps.Web/Lithnet.Laps.Web/Lithnet.Laps.Web.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@
396396
<Content Include="Scripts\popper-utils.js.map" />
397397
<Content Include="Views\Lap\Show.cshtml" />
398398
<Content Include="Views\Home\AuthNError.cshtml" />
399+
<Content Include="Views\Home\LogOut.cshtml" />
399400
</ItemGroup>
400401
<ItemGroup />
401402
<ItemGroup>

src/Lithnet.Laps.Web/Lithnet.Laps.Web/Startup.cs

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
1-
using Microsoft.Owin;
2-
using Owin;
3-
using Microsoft.Owin.Security;
4-
using Microsoft.Owin.Security.Cookies;
5-
using Microsoft.Owin.Security.OpenIdConnect;
6-
using System.Threading.Tasks;
7-
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
8-
using System.Configuration;
9-
using System.Security.Claims;
10-
using IdentityModel.Client;
11-
using System;
1+
using System;
122
using System.Collections.Generic;
3+
using System.Configuration;
134
using System.DirectoryServices.AccountManagement;
14-
using System.Threading;
5+
using System.Security.Claims;
6+
using System.Threading.Tasks;
157
using System.Web;
168
using System.Web.Helpers;
9+
using IdentityModel.Client;
1710
using Lithnet.Laps.Web.App_LocalResources;
1811
using Microsoft.IdentityModel.Logging;
19-
using Microsoft.IdentityModel.Protocols.WsFederation;
12+
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
2013
using Microsoft.IdentityModel.Tokens;
2114
using Microsoft.Owin.Host.SystemWeb;
15+
using Microsoft.Owin.Security;
16+
using Microsoft.Owin.Security.Cookies;
2217
using Microsoft.Owin.Security.Notifications;
18+
using Microsoft.Owin.Security.OpenIdConnect;
2319
using Microsoft.Owin.Security.WsFederation;
2420
using NLog;
21+
using Owin;
2522

2623
namespace Lithnet.Laps.Web
2724
{
2825
public class Startup
2926
{
27+
internal static bool CanLogout = false;
3028
internal static string ClaimName { get; set; } = "upn";
3129

3230
internal static IdentityType ClaimType { get; set; } = IdentityType.UserPrincipalName;
@@ -39,12 +37,15 @@ public class Startup
3937
private readonly string postLogoutRedirectUri = ConfigurationManager.AppSettings["oidc:PostLogoutRedirectUri"];
4038

4139
private readonly string realm = ConfigurationManager.AppSettings["ida:wtrealm"];
40+
private readonly string signOutWreply = ConfigurationManager.AppSettings["ida:signOutWreply"];
4241
private readonly string metadata = ConfigurationManager.AppSettings["ida:metadata"];
4342

4443
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
4544

4645
public void ConfigureOpenIDConnect(IAppBuilder app)
4746
{
47+
Startup.CanLogout = true;
48+
4849
Startup.ClaimName = ConfigurationManager.AppSettings["oidc:claimName"] ?? ClaimTypes.Upn;
4950

5051
if (Enum.TryParse(ConfigurationManager.AppSettings["oidc:claimType"], out IdentityType claimType))
@@ -56,7 +57,7 @@ public void ConfigureOpenIDConnect(IAppBuilder app)
5657
Startup.ClaimType = IdentityType.UserPrincipalName;
5758
}
5859

59-
AntiForgeryConfig.UniqueClaimTypeIdentifier = ConfigurationManager.AppSettings["oidc:uniqueClaimTypeIdentifier"] ?? ClaimTypes.NameIdentifier;
60+
AntiForgeryConfig.UniqueClaimTypeIdentifier = ConfigurationManager.AppSettings["oidc:uniqueClaimTypeIdentifier"] ?? ClaimTypes.PrimarySid;
6061

6162
string responseType = ConfigurationManager.AppSettings["oidc:responseType"] ?? OpenIdConnectResponseType.IdToken;
6263

@@ -76,10 +77,11 @@ public void ConfigureOpenIDConnect(IAppBuilder app)
7677
RedirectUri = this.redirectUri,
7778
ResponseType = responseType,
7879
Scope = OpenIdConnectScope.OpenIdProfile,
79-
PostLogoutRedirectUri = this.postLogoutRedirectUri,
80+
PostLogoutRedirectUri = this.postLogoutRedirectUri ?? new Uri(new Uri(this.redirectUri.Trim('/', '\\')), "Home/LogOut").ToString(),
8081
TokenValidationParameters = new TokenValidationParameters
8182
{
82-
NameClaimType = "name"
83+
NameClaimType = "name",
84+
SaveSigninToken = true
8385
},
8486

8587
Notifications = new OpenIdConnectAuthenticationNotifications
@@ -88,12 +90,6 @@ public void ConfigureOpenIDConnect(IAppBuilder app)
8890
{
8991
try
9092
{
91-
if (responseType == OpenIdConnectResponseType.IdToken)
92-
{
93-
return;
94-
}
95-
96-
// Exchange code for access and ID tokens
9793
OpenIdConnectConfiguration config = await n.Options.ConfigurationManager.GetConfigurationAsync(n.Request.CallCancelled).ConfigureAwait(false);
9894

9995
TokenClient tokenClient = new TokenClient(config.TokenEndpoint, this.clientId, this.clientSecret);
@@ -124,7 +120,12 @@ public void ConfigureOpenIDConnect(IAppBuilder app)
124120
n.Response.Redirect($"/Home/AuthNError?message={HttpUtility.UrlEncode(ex.Message)}");
125121
}
126122
},
127-
SecurityTokenValidated = Startup.FindClaimIdentityInDirectoryOrFail,
123+
SecurityTokenValidated = n =>
124+
{
125+
ClaimsIdentity user = n.AuthenticationTicket.Identity;
126+
user.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
127+
return Startup.FindClaimIdentityInDirectoryOrFail(n);
128+
},
128129
RedirectToIdentityProvider = n =>
129130
{
130131
// If signing out, add the id_token_hint
@@ -148,12 +149,15 @@ public void ConfigureOpenIDConnect(IAppBuilder app)
148149

149150
public void ConfigureWindowsAuth(IAppBuilder app)
150151
{
152+
Startup.CanLogout = false;
153+
AntiForgeryConfig.UniqueClaimTypeIdentifier = ConfigurationManager.AppSettings["ida:uniqueClaimTypeIdentifier"] ?? ClaimTypes.PrimarySid;
151154
ClaimName = ClaimTypes.PrimarySid;
152155
ClaimType = IdentityType.Sid;
153156
}
154157

155158
public void ConfigureWsFederation(IAppBuilder app)
156159
{
160+
Startup.CanLogout = true;
157161
Startup.ClaimName = ConfigurationManager.AppSettings["ida:claimName"] ?? ClaimTypes.Upn;
158162

159163
if (Enum.TryParse(ConfigurationManager.AppSettings["ida:claimType"], out IdentityType claimType))
@@ -167,7 +171,7 @@ public void ConfigureWsFederation(IAppBuilder app)
167171

168172
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
169173

170-
AntiForgeryConfig.UniqueClaimTypeIdentifier = ConfigurationManager.AppSettings["ida:uniqueClaimTypeIdentifier"] ?? ClaimTypes.NameIdentifier;
174+
AntiForgeryConfig.UniqueClaimTypeIdentifier = ConfigurationManager.AppSettings["ida:uniqueClaimTypeIdentifier"] ?? ClaimTypes.PrimarySid;
171175

172176
app.UseCookieAuthentication(new CookieAuthenticationOptions
173177
{
@@ -182,6 +186,7 @@ public void ConfigureWsFederation(IAppBuilder app)
182186
{
183187
Wtrealm = this.realm,
184188
MetadataAddress = this.metadata,
189+
SignOutWreply = this.signOutWreply ?? new Uri(new Uri(this.realm.Trim('/', '\\')), "Home/LogOut").ToString(),
185190
Notifications = new WsFederationAuthenticationNotifications
186191
{
187192
SecurityTokenValidated = Startup.FindClaimIdentityInDirectoryOrFail,
@@ -193,7 +198,6 @@ public void ConfigureWsFederation(IAppBuilder app)
193198
private static Task HandleAuthNFailed<TMessage, TOptions>(AuthenticationFailedNotification<TMessage, TOptions> context)
194199
{
195200
Reporting.LogErrorEvent(EventIDs.OwinAuthNError, LogMessages.AuthNProviderError, context.Exception);
196-
197201
context.HandleResponse();
198202
context.Response.Redirect($"/Home/AuthNError?message={HttpUtility.UrlEncode(context.Exception?.Message ?? "Unknown error")}");
199203

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@using System.Configuration
2+
@{
3+
ViewBag.Title = "Logged out";
4+
}
5+
6+
<div class="form-container">
7+
<fieldset>
8+
<section>
9+
<div class="center-content">
10+
@UIMessages.LoggedOut
11+
</div>
12+
</section>
13+
</fieldset>
14+
</div>

src/Lithnet.Laps.Web/Lithnet.Laps.Web/Views/Lap/Get.cshtml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
<div class="form-container">
88
@using (Html.BeginForm("Get", "Lap", FormMethod.Post))
99
{
10-
<header>
11-
@UIMessages.HeadingRequestPassword
12-
</header>
10+
<header>
11+
@UIMessages.HeadingRequestPassword
12+
</header>
1313
<fieldset>
1414

1515
<section>
@@ -33,8 +33,10 @@
3333
</fieldset>
3434

3535
<footer>
36-
@Html.AntiForgeryToken()
37-
<button type="submit" class="btn btn-primary">@UIMessages.ButtonRequestPassword</button>
36+
<div class="center-content">
37+
@Html.AntiForgeryToken()
38+
<button type="submit" class="btn btn-primary">@UIMessages.ButtonRequestPassword</button>
39+
</div>
3840
</footer>
3941
}
4042
</div>

src/Lithnet.Laps.Web/Lithnet.Laps.Web/Views/Lap/Show.cshtml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@
4242
</fieldset>
4343

4444
<footer>
45-
@using (Html.BeginForm("Get", "Lap", FormMethod.Get))
46-
{
47-
<button type="submit" class="btn btn-primary">@UIMessages.ButtonRequestAnotherPassword</button>
48-
}
45+
<div class="center-content">
46+
@using (Html.BeginForm("Get", "Lap", FormMethod.Get))
47+
{
48+
<button type="submit" class="btn btn-primary">@UIMessages.ButtonRequestAnotherPassword</button>
49+
}
50+
</div>
4951
</footer>
50-
5152
</div>

0 commit comments

Comments
 (0)