Skip to content

Commit c37c4e0

Browse files
committed
handle cae
1 parent 2e3800e commit c37c4e0

File tree

7 files changed

+91
-22
lines changed

7 files changed

+91
-22
lines changed

2-WebApp-graph-user/2-6-BFF-Proxy/CallGraphBFF/ClientApp/src/AuthProvider.js

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,41 @@ const AuthProviderHOC = (C) =>
1515
await this.getAccount();
1616
}
1717

18-
login = () => {
19-
window.location.replace('api/auth/login');
18+
login = (postLoginRedirectUri, claimsChallenge) => {
19+
let url = "api/auth/login";
20+
21+
const searchParams = new URLSearchParams({});
22+
23+
if (postLoginRedirectUri) {
24+
searchParams.append('postLoginRedirectUri', encodeURIComponent(postLoginRedirectUri));
25+
}
26+
27+
if (claimsChallenge) {
28+
searchParams.append('claimsChallenge', JSON.stringify(claimsChallenge));
29+
}
30+
31+
url = `${url}?${searchParams.toString()}`;
32+
33+
window.location.replace(url);
2034
}
2135

22-
logout = () =>{
36+
logout = (postLogoutRedirectUri) => {
2337
this.setState({ isAuthenticated: false, account: null });
24-
window.location.replace('api/auth/logout');
38+
39+
let url = "api/auth/login";
40+
41+
const searchParams = new URLSearchParams({});
42+
43+
if (postLogoutRedirectUri) {
44+
searchParams.append('postLoginRedirectUri', encodeURIComponent(postLogoutRedirectUri));
45+
}
46+
47+
url = `${url}?${searchParams.toString()}`;
48+
49+
window.location.replace(url);
2550
}
2651

27-
getAccount = async() => {
52+
getAccount = async () => {
2853
const response = await fetch('api/auth/account');
2954
const data = await response.json();
3055

2-WebApp-graph-user/2-6-BFF-Proxy/CallGraphBFF/ClientApp/src/components/FetchGraph.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@ export class FetchGraph extends Component {
4040

4141
if (response.ok) {
4242
const data = await response.json();
43-
this.setState({ profile: data, loading: false });
43+
this.setState({ profile: data, loading: false });
4444
} else if (response.status === 401) {
45-
this.props.login();
45+
if (response.body) {
46+
const claims = await response.json();
47+
this.props.login(window.location.href, claims);
48+
}
49+
this.props.login(window.location.href);
4650
}
4751
} catch (error) {
4852
console.log(error);

2-WebApp-graph-user/2-6-BFF-Proxy/CallGraphBFF/Controllers/AuthController.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
using System.Security.Claims;
2+
using System.Web;
23
using Microsoft.AspNetCore.Authentication;
34
using Microsoft.AspNetCore.Authentication.Cookies;
45
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
56
using Microsoft.AspNetCore.Authorization;
67
using Microsoft.AspNetCore.Mvc;
8+
using static Microsoft.Graph.Constants;
79

810
namespace TodoListBFF.Controllers;
911

1012
[Route("api/[controller]")]
1113
public class AuthController : Controller
1214
{
1315
[HttpGet("login")]
14-
public ActionResult Login()
16+
public ActionResult Login(string? postLoginRedirectUri, string? claimsChallenge)
1517
{
16-
return Challenge(new AuthenticationProperties {
17-
RedirectUri = "/"
18-
});
18+
string redirectUri = !string.IsNullOrEmpty(postLoginRedirectUri) ? HttpUtility
19+
.UrlDecode(postLoginRedirectUri) : "/";
20+
21+
var props = new AuthenticationProperties { RedirectUri = redirectUri };
22+
23+
if (claimsChallenge != null)
24+
{
25+
string jsonString = claimsChallenge
26+
.Replace("\\", "")
27+
.Trim(new char[1] { '"' });
28+
29+
string? loginHint = (this.User.Identity as ClaimsIdentity)?.Claims
30+
.FirstOrDefault(c => c.Type == "login_hint")?.Value;
31+
32+
props.Items["claims"] = jsonString;
33+
props.Items["login_hint"] = loginHint;
34+
}
35+
36+
return Challenge(props);
1937
}
2038

2139
[Authorize]
@@ -24,15 +42,14 @@ public async Task<ActionResult> Logout()
2442
{
2543
await HttpContext.SignOutAsync();
2644

45+
var props = new AuthenticationProperties { RedirectUri = "/" };
46+
2747
List<string> optionList = new List<string> {
2848
CookieAuthenticationDefaults.AuthenticationScheme,
2949
OpenIdConnectDefaults.AuthenticationScheme
3050
};
3151

32-
return new SignOutResult(optionList, new AuthenticationProperties
33-
{
34-
RedirectUri = "/"
35-
});
52+
return new SignOutResult(optionList, props);
3653
}
3754

3855
[HttpGet("account")]

2-WebApp-graph-user/2-6-BFF-Proxy/CallGraphBFF/Controllers/ProfileController.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.AspNetCore.Mvc;
22
using Microsoft.AspNetCore.Authorization;
33
using Microsoft.AspNetCore.Authentication.Cookies;
4+
using Microsoft.Identity.Client;
45
using Microsoft.Identity.Web;
56
using Microsoft.Graph;
67

@@ -25,18 +26,22 @@ public async Task<ActionResult<User>> GetProfile()
2526
{
2627
User profile = await _graphServiceClient.Me
2728
.Request()
28-
.WithScopes("user.read")
2929
.GetAsync();
3030

3131
return Ok(profile);
3232
}
33-
catch (ServiceException svcex) when (svcex.InnerException != null && svcex.InnerException.Message.Contains("MsalUiRequiredException"))
33+
catch (ServiceException svcex)
34+
when (svcex.InnerException is MicrosoftIdentityWebChallengeUserException)
3435
{
35-
return Unauthorized("MsalUiRequiredException occurred. Please sign-in again.\n" + svcex.Message);
36+
return Unauthorized("MicrosoftIdentityWebChallengeUserException occurred.\n" + svcex.Message);
3637
}
37-
catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation"))
38+
catch (ServiceException svcex)
39+
when (svcex.Message.Contains("Continuous access evaluation"))
3840
{
39-
return Unauthorized("Continuous access evaluation challenge occurred. Please sign-in again.\n" + svcex.Message);
41+
string claimChallenge = WwwAuthenticateParameters
42+
.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
43+
44+
return Unauthorized(claimChallenge);
4045
}
4146
catch (Exception ex)
4247
{

2-WebApp-graph-user/2-6-BFF-Proxy/CallGraphBFF/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
options.Cookie.HttpOnly = true;
2424
options.Cookie.IsEssential = true;
2525

26+
options.Events = new RespondUnauthorizedWhenSessionCookieNotFoundEvents();
2627
options.Events = new RejectSessionCookieWhenAccountNotInCacheEvents();
2728
});
2829

2-WebApp-graph-user/2-6-BFF-Proxy/CallGraphBFF/Utils/RejectSessionCookieWhenAccountNotInCacheEvents.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ public async override Task ValidatePrincipal(CookieValidatePrincipalContext cont
99
try
1010
{
1111
var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
12+
1213
string token = await tokenAcquisition.GetAccessTokenForUserAsync(
1314
scopes: new[] { "user.read" },
1415
user: context.Principal);
1516
}
16-
catch (MicrosoftIdentityWebChallengeUserException ex)
17-
when (AccountDoesNotExitInTokenCache(ex))
17+
catch (MicrosoftIdentityWebChallengeUserException ex) when (AccountDoesNotExitInTokenCache(ex))
1818
{
1919
context.RejectPrincipal();
2020
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.AspNetCore.Authentication;
2+
using Microsoft.AspNetCore.Authentication.Cookies;
3+
4+
internal class RespondUnauthorizedWhenSessionCookieNotFoundEvents : CookieAuthenticationEvents
5+
{
6+
public override Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context)
7+
{
8+
context.Response.StatusCode = 401;
9+
return Task.CompletedTask;
10+
}
11+
12+
public override Task RedirectToAccessDenied(RedirectContext<CookieAuthenticationOptions> context)
13+
{
14+
context.Response.StatusCode = 401;
15+
return Task.CompletedTask;
16+
}
17+
}

0 commit comments

Comments
 (0)