|
18 | 18 | using Microsoft.AspNetCore.WebUtilities;
|
19 | 19 | using Microsoft.Extensions.Logging;
|
20 | 20 | using Microsoft.Extensions.Options;
|
| 21 | +using Microsoft.Extensions.Primitives; |
21 | 22 |
|
22 | 23 | namespace AspNet.Security.OAuth.LinkedIn
|
23 | 24 | {
|
@@ -115,5 +116,60 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
|
115 | 116 | .Select((p) => p.GetString("emailAddress"))
|
116 | 117 | .FirstOrDefault();
|
117 | 118 | }
|
| 119 | + |
| 120 | + protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync() |
| 121 | + { |
| 122 | + // Taken from the base class' implementation so we can modify the status code check |
| 123 | + // for "access denied" as LinkedIn uses non-standard error codes (see https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/issues/480). |
| 124 | + // https://github.com/dotnet/aspnetcore/blob/bbf7c8780c42e3d32aeec8018367037784eb9181/src/Security/Authentication/OAuth/src/OAuthHandler.cs#L49-L87 |
| 125 | + var query = Request.Query; |
| 126 | + |
| 127 | + var state = query["state"]; |
| 128 | + var properties = Options.StateDataFormat.Unprotect(state); |
| 129 | + |
| 130 | + if (properties == null) |
| 131 | + { |
| 132 | + return HandleRequestResult.Fail("The oauth state was missing or invalid."); |
| 133 | + } |
| 134 | + |
| 135 | + // OAuth2 10.12 CSRF |
| 136 | + if (!ValidateCorrelationId(properties)) |
| 137 | + { |
| 138 | + return HandleRequestResult.Fail("Correlation failed.", properties); |
| 139 | + } |
| 140 | + |
| 141 | + var error = query["error"]; |
| 142 | + if (!StringValues.IsNullOrEmpty(error)) |
| 143 | + { |
| 144 | + // Note: access_denied errors are special protocol errors indicating the user didn't |
| 145 | + // approve the authorization demand requested by the remote authorization server. |
| 146 | + // Since it's a frequent scenario (that is not caused by incorrect configuration), |
| 147 | + // denied errors are handled differently using HandleAccessDeniedErrorAsync(). |
| 148 | + // Visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information. |
| 149 | + var errorDescription = query["error_description"]; |
| 150 | + var errorUri = query["error_uri"]; |
| 151 | + |
| 152 | + // See https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow#application-is-rejected |
| 153 | + if (StringValues.Equals(error, "access_denied") || |
| 154 | + StringValues.Equals(error, "user_cancelled_login") || |
| 155 | + StringValues.Equals(error, "user_cancelled_authorize")) |
| 156 | + { |
| 157 | + var result = await HandleAccessDeniedErrorAsync(properties); |
| 158 | + if (!result.None) |
| 159 | + { |
| 160 | + return result; |
| 161 | + } |
| 162 | + |
| 163 | + var deniedEx = new Exception("Access was denied by the resource owner or by the remote server."); |
| 164 | + deniedEx.Data["error"] = error.ToString(); |
| 165 | + deniedEx.Data["error_description"] = errorDescription.ToString(); |
| 166 | + deniedEx.Data["error_uri"] = errorUri.ToString(); |
| 167 | + |
| 168 | + return HandleRequestResult.Fail(deniedEx, properties); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + return await base.HandleRemoteAuthenticateAsync(); |
| 173 | + } |
118 | 174 | }
|
119 | 175 | }
|
0 commit comments