Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Commit 88e91f6

Browse files
committed
#1188 Add AuthenticationProperties to HandleRequestResult and RemoteFailureContext
1 parent 13fdbac commit 88e91f6

File tree

11 files changed

+360
-176
lines changed

11 files changed

+360
-176
lines changed

samples/SocialSample/Startup.cs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public void ConfigureServices(IServiceCollection services)
6767
o.Fields.Add("name");
6868
o.Fields.Add("email");
6969
o.SaveTokens = true;
70+
o.Events = new OAuthEvents()
71+
{
72+
OnRemoteFailure = HandleOnRemoteFailure
73+
};
7074
})
7175
// You must first create an app with Google and add its ID and Secret to your user-secrets.
7276
// https://console.developers.google.com/project
@@ -81,6 +85,10 @@ public void ConfigureServices(IServiceCollection services)
8185
o.Scope.Add("profile");
8286
o.Scope.Add("email");
8387
o.SaveTokens = true;
88+
o.Events = new OAuthEvents()
89+
{
90+
OnRemoteFailure = HandleOnRemoteFailure
91+
};
8492
})
8593
// You must first create an app with Google and add its ID and Secret to your user-secrets.
8694
// https://console.developers.google.com/project
@@ -93,12 +101,7 @@ public void ConfigureServices(IServiceCollection services)
93101
o.SaveTokens = true;
94102
o.Events = new OAuthEvents()
95103
{
96-
OnRemoteFailure = ctx =>
97-
{
98-
ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message));
99-
ctx.HandleResponse();
100-
return Task.FromResult(0);
101-
}
104+
OnRemoteFailure = HandleOnRemoteFailure
102105
};
103106
o.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url");
104107
o.ClaimActions.Remove(ClaimTypes.GivenName);
@@ -116,12 +119,7 @@ public void ConfigureServices(IServiceCollection services)
116119
o.ClaimActions.MapJsonKey("urn:twitter:profilepicture", "profile_image_url", ClaimTypes.Uri);
117120
o.Events = new TwitterEvents()
118121
{
119-
OnRemoteFailure = ctx =>
120-
{
121-
ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message));
122-
ctx.HandleResponse();
123-
return Task.FromResult(0);
124-
}
122+
OnRemoteFailure = HandleOnRemoteFailure
125123
};
126124
})
127125
/* Azure AD app model v2 has restrictions that prevent the use of plain HTTP for redirect URLs.
@@ -139,6 +137,10 @@ public void ConfigureServices(IServiceCollection services)
139137
o.TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint;
140138
o.Scope.Add("https://graph.microsoft.com/user.read");
141139
o.SaveTokens = true;
140+
o.Events = new OAuthEvents()
141+
{
142+
OnRemoteFailure = HandleOnRemoteFailure
143+
};
142144
})
143145
// You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets.
144146
// https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-app-registration/
@@ -148,6 +150,10 @@ public void ConfigureServices(IServiceCollection services)
148150
o.ClientSecret = Configuration["microsoftaccount:clientsecret"];
149151
o.SaveTokens = true;
150152
o.Scope.Add("offline_access");
153+
o.Events = new OAuthEvents()
154+
{
155+
OnRemoteFailure = HandleOnRemoteFailure
156+
};
151157
})
152158
// You must first create an app with GitHub and add its ID and Secret to your user-secrets.
153159
// https://github.com/settings/applications/
@@ -159,6 +165,10 @@ public void ConfigureServices(IServiceCollection services)
159165
o.AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
160166
o.TokenEndpoint = "https://github.com/login/oauth/access_token";
161167
o.SaveTokens = true;
168+
o.Events = new OAuthEvents()
169+
{
170+
OnRemoteFailure = HandleOnRemoteFailure
171+
};
162172
})
163173
// You must first create an app with GitHub and add its ID and Secret to your user-secrets.
164174
// https://github.com/settings/applications/
@@ -180,6 +190,7 @@ public void ConfigureServices(IServiceCollection services)
180190
o.ClaimActions.MapJsonKey("urn:github:url", "url");
181191
o.Events = new OAuthEvents
182192
{
193+
OnRemoteFailure = HandleOnRemoteFailure,
183194
OnCreatingTicket = async context =>
184195
{
185196
// Get the GitHub user
@@ -198,6 +209,30 @@ public void ConfigureServices(IServiceCollection services)
198209
});
199210
}
200211

212+
private async Task HandleOnRemoteFailure(RemoteFailureContext context)
213+
{
214+
context.Response.StatusCode = 500;
215+
context.Response.ContentType = "text/html";
216+
await context.Response.WriteAsync("<html><body>");
217+
await context.Response.WriteAsync("A remote failure has occurred: " + UrlEncoder.Default.Encode(context.Failure.Message) + "<br>");
218+
219+
if (context.Properties != null)
220+
{
221+
await context.Response.WriteAsync("Properties:<br>");
222+
foreach (var pair in context.Properties.Items)
223+
{
224+
await context.Response.WriteAsync($"-{ UrlEncoder.Default.Encode(pair.Key)}={ UrlEncoder.Default.Encode(pair.Value)}<br>");
225+
}
226+
}
227+
228+
await context.Response.WriteAsync("<a href=\"/\">Home</a>");
229+
await context.Response.WriteAsync("</body></html>");
230+
231+
// context.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(context.Failure.Message));
232+
233+
context.HandleResponse();
234+
}
235+
201236
public void Configure(IApplicationBuilder app)
202237
{
203238
app.UseDeveloperExceptionPage();

src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,22 @@ public OAuthHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, Ur
4444

4545
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
4646
{
47-
AuthenticationProperties properties = null;
4847
var query = Request.Query;
4948

49+
var state = query["state"];
50+
var properties = Options.StateDataFormat.Unprotect(state);
51+
52+
if (properties == null)
53+
{
54+
return HandleRequestResult.Fail("The oauth state was missing or invalid.");
55+
}
56+
57+
// OAuth2 10.12 CSRF
58+
if (!ValidateCorrelationId(properties))
59+
{
60+
return HandleRequestResult.Fail("Correlation failed.", properties);
61+
}
62+
5063
var error = query["error"];
5164
if (!StringValues.IsNullOrEmpty(error))
5265
{
@@ -63,39 +76,26 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
6376
failureMessage.Append(";Uri=").Append(errorUri);
6477
}
6578

66-
return HandleRequestResult.Fail(failureMessage.ToString());
79+
return HandleRequestResult.Fail(failureMessage.ToString(), properties);
6780
}
6881

6982
var code = query["code"];
70-
var state = query["state"];
71-
72-
properties = Options.StateDataFormat.Unprotect(state);
73-
if (properties == null)
74-
{
75-
return HandleRequestResult.Fail("The oauth state was missing or invalid.");
76-
}
77-
78-
// OAuth2 10.12 CSRF
79-
if (!ValidateCorrelationId(properties))
80-
{
81-
return HandleRequestResult.Fail("Correlation failed.");
82-
}
8383

8484
if (StringValues.IsNullOrEmpty(code))
8585
{
86-
return HandleRequestResult.Fail("Code was not found.");
86+
return HandleRequestResult.Fail("Code was not found.", properties);
8787
}
8888

8989
var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath));
9090

9191
if (tokens.Error != null)
9292
{
93-
return HandleRequestResult.Fail(tokens.Error);
93+
return HandleRequestResult.Fail(tokens.Error, properties);
9494
}
9595

9696
if (string.IsNullOrEmpty(tokens.AccessToken))
9797
{
98-
return HandleRequestResult.Fail("Failed to retrieve access token.");
98+
return HandleRequestResult.Fail("Failed to retrieve access token.", properties);
9999
}
100100

101101
var identity = new ClaimsIdentity(ClaimsIssuer);
@@ -141,7 +141,7 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
141141
}
142142
else
143143
{
144-
return HandleRequestResult.Fail("Failed to retrieve user information from remote server.");
144+
return HandleRequestResult.Fail("Failed to retrieve user information from remote server.", properties);
145145
}
146146
}
147147

src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -491,12 +491,19 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
491491
return HandleRequestResult.Fail("No message.");
492492
}
493493

494+
AuthenticationProperties properties = null;
494495
try
495496
{
496-
AuthenticationProperties properties = null;
497497
if (!string.IsNullOrEmpty(authorizationResponse.State))
498498
{
499499
properties = Options.StateDataFormat.Unprotect(authorizationResponse.State);
500+
501+
if (properties != null)
502+
{
503+
// If properties can be decoded from state, clear the message state.
504+
properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out var userstate);
505+
authorizationResponse.State = userstate;
506+
}
500507
}
501508

502509
var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse, properties);
@@ -523,6 +530,13 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
523530

524531
// if state exists and we failed to 'unprotect' this is not a message we should process.
525532
properties = Options.StateDataFormat.Unprotect(authorizationResponse.State);
533+
534+
if (properties != null)
535+
{
536+
// If properties can be decoded from state, clear the message state.
537+
properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out var userstate);
538+
authorizationResponse.State = userstate;
539+
}
526540
}
527541

528542
if (properties == null)
@@ -536,18 +550,15 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
536550
return HandleRequestResult.Fail(Resources.MessageStateIsInvalid);
537551
}
538552

539-
properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out string userstate);
540-
authorizationResponse.State = userstate;
541-
542553
if (!ValidateCorrelationId(properties))
543554
{
544-
return HandleRequestResult.Fail("Correlation failed.");
555+
return HandleRequestResult.Fail("Correlation failed.", properties);
545556
}
546557

547558
// if any of the error fields are set, throw error null
548559
if (!string.IsNullOrEmpty(authorizationResponse.Error))
549560
{
550-
return HandleRequestResult.Fail(CreateOpenIdConnectProtocolException(authorizationResponse, response: null));
561+
return HandleRequestResult.Fail(CreateOpenIdConnectProtocolException(authorizationResponse, response: null), properties);
551562
}
552563

553564
if (_configuration == null && Options.ConfigurationManager != null)
@@ -635,8 +646,7 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
635646

636647
// At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response.
637648
// And we'll want to validate the new JWT in ValidateTokenResponse.
638-
JwtSecurityToken tokenEndpointJwt;
639-
var tokenEndpointUser = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out tokenEndpointJwt);
649+
var tokenEndpointUser = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out var tokenEndpointJwt);
640650

641651
// Avoid reading & deleting the nonce cookie, running the event, etc, if it was already done as part of the authorization response validation.
642652
if (user == null)
@@ -722,7 +732,7 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
722732
return authenticationFailedContext.Result;
723733
}
724734

725-
return HandleRequestResult.Fail(exception);
735+
return HandleRequestResult.Fail(exception, properties);
726736
}
727737
}
728738

@@ -830,7 +840,7 @@ protected virtual async Task<HandleRequestResult> GetUserInformationAsync(
830840
}
831841
else
832842
{
833-
return HandleRequestResult.Fail("Unknown response type: " + contentType.MediaType);
843+
return HandleRequestResult.Fail("Unknown response type: " + contentType.MediaType, properties);
834844
}
835845

836846
var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(principal, properties, message, user);

src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public TwitterHandler(IOptionsMonitor<TwitterOptions> options, ILoggerFactory lo
4646

4747
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
4848
{
49-
AuthenticationProperties properties = null;
5049
var query = Request.Query;
5150
var protectedRequestToken = Request.Cookies[Options.StateCookie.Name];
5251

@@ -57,25 +56,25 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
5756
return HandleRequestResult.Fail("Invalid state cookie.");
5857
}
5958

60-
properties = requestToken.Properties;
59+
var properties = requestToken.Properties;
6160

6261
// REVIEW: see which of these are really errors
6362

6463
var returnedToken = query["oauth_token"];
6564
if (StringValues.IsNullOrEmpty(returnedToken))
6665
{
67-
return HandleRequestResult.Fail("Missing oauth_token");
66+
return HandleRequestResult.Fail("Missing oauth_token", properties);
6867
}
6968

7069
if (!string.Equals(returnedToken, requestToken.Token, StringComparison.Ordinal))
7170
{
72-
return HandleRequestResult.Fail("Unmatched token");
71+
return HandleRequestResult.Fail("Unmatched token", properties);
7372
}
7473

7574
var oauthVerifier = query["oauth_verifier"];
7675
if (StringValues.IsNullOrEmpty(oauthVerifier))
7776
{
78-
return HandleRequestResult.Fail("Missing or blank oauth_verifier");
77+
return HandleRequestResult.Fail("Missing or blank oauth_verifier", properties);
7978
}
8079

8180
var cookieOptions = Options.StateCookie.Build(Context, Clock.UtcNow);

src/Microsoft.AspNetCore.Authentication/Events/RemoteFailureContext.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Authentication
1111
/// </summary>
1212
public class RemoteFailureContext : HandleRequestContext<RemoteAuthenticationOptions>
1313
{
14+
[Obsolete]
1415
public RemoteFailureContext(
1516
HttpContext context,
1617
AuthenticationScheme scheme,
@@ -21,9 +22,31 @@ public RemoteFailureContext(
2122
Failure = failure;
2223
}
2324

25+
public RemoteFailureContext(
26+
HttpContext context,
27+
AuthenticationScheme scheme,
28+
RemoteAuthenticationOptions options)
29+
: base(context, scheme, options)
30+
{
31+
}
32+
2433
/// <summary>
2534
/// User friendly error message for the error.
2635
/// </summary>
27-
public Exception Failure { get; set; }
36+
public Exception Failure
37+
{
38+
get => Error?.Failure;
39+
set => Error = value == null ? new AuthenticationError(value, Properties) : null;
40+
}
41+
42+
/// <summary>
43+
/// Additional state values for the authentication session.
44+
/// </summary>
45+
public AuthenticationProperties Properties => Error?.Properties;
46+
47+
/// <summary>
48+
/// Holds error information from the authentication.
49+
/// </summary>
50+
public AuthenticationError Error { get; set; }
2851
}
2952
}

0 commit comments

Comments
 (0)