protected override async Task <OAuthTokenResponse> ExchangeCodeAsync([NotNull] OAuthCodeExchangeContext context) { var tokenRequestParameters = new Dictionary <string, string> { ["grant_type"] = "authorization_code", ["redirect_uri"] = context.RedirectUri, ["code"] = context.Code, }; // PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see BuildChallengeUrl if (context.Properties.Items.TryGetValue(OAuthConstants.CodeVerifierKey, out var codeVerifier)) { tokenRequestParameters.Add(OAuthConstants.CodeVerifierKey, codeVerifier !); context.Properties.Items.Remove(OAuthConstants.CodeVerifierKey); } using var request = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = CreateAuthorizationHeader(); // When a custom user agent is specified in the options, add it to the request headers // to override the default (generic) user agent used by the OAuth2 base middleware. if (!string.IsNullOrEmpty(Options.UserAgent)) { request.Headers.UserAgent.ParseAdd(Options.UserAgent); } request.Content = new FormUrlEncodedContent(tokenRequestParameters); using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { await Log.ExchangeCodeErrorAsync(Logger, response, Context.RequestAborted); return(OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."))); } var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); return(OAuthTokenResponse.Success(payload)); }
protected virtual async Task <string> GetEmailAsync([NotNull] OAuthTokenResponse tokens) { // See https://developer.github.com/v3/users/emails/ for more information about the /user/emails endpoint. var request = new HttpRequestMessage(HttpMethod.Get, Options.UserEmailsEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); // Failed requests shouldn't cause an error: in this case, return null to indicate that the email address cannot be retrieved. var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { return(null); } var payload = JArray.Parse(await response.Content.ReadAsStringAsync()); return(GitHubAuthenticationHelper.GetEmail(payload)); }
/// <inheritdoc /> protected override async Task <OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri) { var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, new Dictionary <string, string> { { "grant_type", "authorization_code" }, { "client_id", Options.ClientId }, { "client_secret", MakeBankIdClientSecret(Options.ClientId, Options.ClientSecret, code) }, { nameof(code), code }, { "redirect_uri", redirectUri }, }); var response = await Backchannel.SendAsync(new HttpRequestMessage(HttpMethod.Get, address) { Headers = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } } }, Context.RequestAborted); return(response.IsSuccessStatusCode ? OAuthTokenResponse.Success(JObject.Parse(await response.Content.ReadAsStringAsync())) : OAuthTokenResponse.Failed(new Exception("OAuth token endpoint failure: " + await Display(response)))); }
protected async Task <string?> GetEmailAsync([NotNull] OAuthTokenResponse tokens) { using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserEmailsEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { await Log.EmailAddressErrorAsync(Logger, response, Context.RequestAborted); throw new HttpRequestException("An error occurred while retrieving the email address associated to the user profile."); } using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); return((from address in payload.RootElement.EnumerateArray() select address.GetString("email")).FirstOrDefault()); }
protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); response.EnsureSuccessStatusCode(); var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); identity.AddOptionalClaim(ClaimTypes.NameIdentifier, GitHubAuthenticationHelper.GetIdentifier(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Name, GitHubAuthenticationHelper.GetLogin(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Email, GitHubAuthenticationHelper.GetEmail(payload), Options.ClaimsIssuer) .AddOptionalClaim("urn:github:name", GitHubAuthenticationHelper.GetName(payload), Options.ClaimsIssuer) .AddOptionalClaim("urn:github:url", GitHubAuthenticationHelper.GetLink(payload), Options.ClaimsIssuer); // When the email address is not public, retrieve it from the emails endpoint if the user:email scope is specified. if (!identity.HasClaim(claim => claim.Type == ClaimTypes.Email) && Options.Scope.Contains("user:email")) { identity.AddOptionalClaim(ClaimTypes.Email, await GetEmailAsync(tokens), Options.ClaimsIssuer); } var context = new OAuthCreatingTicketContext(Context, Options, Backchannel, tokens, payload) { Principal = new ClaimsPrincipal(identity), Properties = properties }; await Options.Events.CreatingTicket(context); if (context.Principal?.Identity == null) { return(null); } return(new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme)); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri) { var tokenRequestParameters = new Dictionary <string, string>() { { "client_id", Options.ClientId }, { "redirect_uri", redirectUri }, { "client_secret", Options.ClientSecret }, { "code", code } }; var requestContent = new FormUrlEncodedContent(tokenRequestParameters); var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); requestMessage.Content = requestContent; var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); if (response.IsSuccessStatusCode) { var payloadObject = JObject.Parse(await response.Content.ReadAsStringAsync()); var payload = new JObject { ["access_token"] = payloadObject.Property("access_token").Value["token"], ["token_type"] = "code", ["refresh_token"] = payloadObject.Property("access_token").Value["token"], ["expires_in"] = payloadObject.Property("access_token").Value["expires_at"] }; return(OAuthTokenResponse.Success(payload)); } else { var error = new StringBuilder(); error.Append("OAuth token endpoint failure: "); error.Append("Status: " + response.StatusCode + ";"); error.Append("Headers: " + response.Headers.ToString() + ";"); error.Append("Body: " + await response.Content.ReadAsStringAsync() + ";"); return(OAuthTokenResponse.Failed(new Exception(error.ToString()))); } }
/// <inheritdoc /> protected override async Task <AuthenticationTicket> CreateTicketAsync( [NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { var contextId = await ProcessIdTokenAndGetContactIdentifierAsync(tokens, properties, identity); if (string.IsNullOrEmpty(contextId)) { throw new InvalidOperationException("An error occurred trying to obtain the context identifier from the current user's identity claims."); } // Add contextId to the Options.UserInformationEndpoint (https://sod.superoffice.com/{0}/api/v1/user/currentPrincipal). var userInfoEndpoint = string.Format(CultureInfo.InvariantCulture, Options.UserInformationEndpoint, contextId); // Get the SuperOffice user principal. using var request = new HttpRequestMessage(HttpMethod.Get, userInfoEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted); throw new HttpRequestException($"An error occurred when retrieving SuperOffice user information ({response.StatusCode})."); } using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); context.RunClaimActions(); await Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name)); }
protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, "access_token", tokens.AccessToken); if (Options.UseSignedRequests) { // Compute the HMAC256 signature. var signature = ComputeSignature(address); // Add the signature to the query string. address = QueryHelpers.AddQueryString(address, "sig", signature); } using var request = new HttpRequestMessage(HttpMethod.Get, address); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving the user profile: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); throw new HttpRequestException("An error occurred while retrieving the user profile."); } using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); context.RunClaimActions(payload.RootElement.GetProperty("data")); await Options.Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name)); }
protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { HttpRequestMessage request = null; HttpResponseMessage response = null; try { request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving the user profile: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); throw new HttpRequestException("An error occurred while retrieving the user profile."); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload); context.RunClaimActions(payload); await Options.Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name)); } finally { request?.Dispose(); response?.Dispose(); } }
protected virtual async Task <string> GetEmailAsync([NotNull] OAuthTokenResponse tokens) { // See http://open.weibo.com/wiki/2/account/profile/email for more information about the /account/profile/email.json endpoint. var address = QueryHelpers.AddQueryString(Options.UserEmailsEndpoint, new Dictionary <string, string> { ["access_token"] = tokens.AccessToken }); HttpRequestMessage request = null; HttpResponseMessage response = null; try { request = new HttpRequestMessage(HttpMethod.Get, address); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // Failed requests shouldn't cause an error: in this case, return null to indicate that the email address cannot be retrieved. response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogWarning("An error occurred while retrieving the email address associated with the logged in user: "******"the remote server returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); return(null); } var payload = JArray.Parse(await response.Content.ReadAsStringAsync()); return((from email in payload.AsJEnumerable() select email.Value <string>("email")).FirstOrDefault()); } finally { request?.Dispose(); response?.Dispose(); } }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync([NotNull] OAuthCodeExchangeContext context) { using var request = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var parameters = new Dictionary <string, string> { ["client_id"] = Options.ClientId, ["redirect_uri"] = context.RedirectUri, ["client_secret"] = Options.ClientSecret, ["code"] = context.Code, ["grant_type"] = "authorization_code" }; request.Content = new FormUrlEncodedContent(parameters !); using var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { await Log.ExchangeCodeErrorAsync(Logger, response, Context.RequestAborted); return(OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."))); } // Note: Yammer doesn't return a standard OAuth2 response. To make this middleware compatible // with the OAuth2 generic middleware, a compliant JSON payload is generated manually. // See https://developer.yammer.com/docs/oauth-2 for more information about this process. using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); string?accessToken = payload.RootElement.GetProperty("access_token").GetString("token"); var token = new { access_token = accessToken, token_type = string.Empty, refresh_token = string.Empty, expires_in = string.Empty, }; return(OAuthTokenResponse.Success(JsonSerializer.SerializeToDocument(token))); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync([NotNull] OAuthCodeExchangeContext context) { var tokenRequestParameters = new Dictionary <string, string>() { ["client_id"] = Options.ClientId, ["redirect_uri"] = context.RedirectUri, ["client_secret"] = Options.ClientSecret, ["code"] = context.Code, ["grant_type"] = "authorization_code", }; // PKCE https://tools.ietf.org/html/rfc7636#section-4.5 if (context.Properties.Items.TryGetValue(OAuthConstants.CodeVerifierKey, out var codeVerifier)) { tokenRequestParameters.Add(OAuthConstants.CodeVerifierKey, codeVerifier !); context.Properties.Items.Remove(OAuthConstants.CodeVerifierKey); } string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes( string.Concat( Uri.EscapeDataString(Options.ClientId), ":", Uri.EscapeDataString(Options.ClientSecret)))); using var requestContent = new FormUrlEncodedContent(tokenRequestParameters !); using var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials); requestMessage.Content = requestContent; requestMessage.Version = Backchannel.DefaultRequestVersion; using var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); var body = await response.Content.ReadAsStringAsync(); return(response.IsSuccessStatusCode switch { true => OAuthTokenResponse.Success(JsonDocument.Parse(body)), false => PrepareFailedOAuthTokenReponse(response, body) });
private async Task <string> GetUserIdentifierAsync(OAuthTokenResponse tokens) { var address = QueryHelpers.AddQueryString(Options.UserIdentificationEndpoint, "access_token", tokens.AccessToken); HttpRequestMessage request = null; HttpResponseMessage response = null; try { request = new HttpRequestMessage(HttpMethod.Get, address); response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving the user identifier: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); throw new HttpRequestException("An error occurred while retrieving the user identifier."); } var body = await response.Content.ReadAsStringAsync(); var index = body.IndexOf("{"); if (index > 0) { body = body.Substring(index, body.LastIndexOf("}") - index + 1); } var payload = JObject.Parse(body); return(payload.Value <string>("openid")); } finally { request?.Dispose(); response?.Dispose(); } }
protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { // See https://developer.foursquare.com/overview/versioning // for more information about the mandatory "v" and "m" parameters. var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary <string, string> { ["m"] = "foursquare", ["v"] = Options.ApiVersion, ["oauth_token"] = tokens.AccessToken, }); var request = new HttpRequestMessage(HttpMethod.Get, address); var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); response.EnsureSuccessStatusCode(); var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); identity.AddOptionalClaim(ClaimTypes.NameIdentifier, FoursquareAuthenticationHelper.GetIdentifier(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Surname, FoursquareAuthenticationHelper.GetLastName(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.GivenName, FoursquareAuthenticationHelper.GetFirstName(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Name, FoursquareAuthenticationHelper.GetUserName(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Gender, FoursquareAuthenticationHelper.GetGender(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Email, FoursquareAuthenticationHelper.GetContactEmail(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Uri, FoursquareAuthenticationHelper.GetCanonicalUrl(payload), Options.ClaimsIssuer); var context = new OAuthCreatingTicketContext(Context, Options, Backchannel, tokens, payload) { Principal = new ClaimsPrincipal(identity), Properties = properties }; await Options.Events.CreatingTicket(context); if (context.Principal?.Identity == null) { return(null); } return(new AuthenticationTicket(context.Principal, context.Properties, context.Options.AuthenticationScheme)); }
protected override async Task <AuthenticationTicket> CreateTicketAsync( [NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { // Note: the ArcGIS API doesn't support content negotiation via headers. string address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary <string, string> { ["f"] = "json", ["token"] = tokens.AccessToken }); using var request = new HttpRequestMessage(HttpMethod.Get, address); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // Request the token using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); // Note: error responses always return 200 status codes. if (payload.RootElement.TryGetProperty("error", out var error)) { // See https://developers.arcgis.com/authentication/server-based-user-logins/ for more information Logger.LogError("An error occurred while retrieving the user profile: the remote server " + "returned a response with the following error code: {Code} {Message}.", /* Code: */ error.GetString("code"), /* Message: */ error.GetString("message")); throw new InvalidOperationException("An error occurred while retrieving the user profile."); } var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); context.RunClaimActions(); await Options.Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name)); }
protected override async Task <AuthenticationTicket> CreateTicketAsync( [NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { string endpoint = Options.UserInformationEndpoint; if (Options.Fields.Count > 0) { endpoint = QueryHelpers.AddQueryString(endpoint, "fields[user]", string.Join(',', Options.Fields)); } if (Options.Includes.Count > 0) { endpoint = QueryHelpers.AddQueryString(endpoint, "include", string.Join(',', Options.Includes)); } using var request = new HttpRequestMessage(HttpMethod.Get, endpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted); throw new HttpRequestException("An error occurred while retrieving the user profile."); } using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); context.RunClaimActions(payload.RootElement.GetProperty("data")); await Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name)); }
/// <inheritdoc/> protected override async Task <OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } using var request = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded")); request.Headers.UserAgent.ParseAdd(OneIdAuthenticationDefaults.UserAgent); var parameters = new Dictionary <string, string> { ["redirect_uri"] = context.RedirectUri, ["grant_type"] = "authorization_code", ["client_id"] = Options.ClientId, ["code"] = context.Code, ["code_verifier"] = context.Properties.Items["code_verifier"] }; request.Content = new FormUrlEncodedContent(parameters); using var response = await Backchannel.SendAsync(request, Context.RequestAborted).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { string errorBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); Logger.LogError("An error occurred while retrieving an access token: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ errorBody); return(OAuthTokenResponse.Failed(new OneIdAuthenticationException($"An error occurred while retrieving an access token. The remote server returned a {response.StatusCode} response with the following payload: {response.Headers.ToString()} {errorBody}"))); } var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); return(OAuthTokenResponse.Success(payload)); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync([NotNull] OAuthCodeExchangeContext context) { var tokenRequestParameters = new Dictionary <string, string?>() { ["app_id"] = Options.ClientId, ["secret"] = Options.ClientSecret, ["code"] = context.Code, ["output"] = "json", }; // PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see BuildChallengeUrl if (context.Properties.Items.TryGetValue(OAuthConstants.CodeVerifierKey, out string?codeVerifier)) { tokenRequestParameters.Add(OAuthConstants.CodeVerifierKey, codeVerifier); context.Properties.Items.Remove(OAuthConstants.CodeVerifierKey); } string endpoint = QueryHelpers.AddQueryString(Options.TokenEndpoint, tokenRequestParameters); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, endpoint); requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); using var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); if (!response.IsSuccessStatusCode) { const string Error = "An error occurred while retrieving an OAuth token"; Logger.LogError("{Error}: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Error: */ Error, /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync(Context.RequestAborted)); return(OAuthTokenResponse.Failed(new Exception(Error))); } var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); return(OAuthTokenResponse.Success(payload)); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync([NotNull] string code, [NotNull] string redirectUri) { var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, new Dictionary <string, string>() { ["client_id"] = Options.ClientId, ["client_secret"] = Options.ClientSecret, ["redirect_uri"] = redirectUri, ["code"] = code, ["grant_type"] = "authorization_code", }); HttpRequestMessage request = null; HttpResponseMessage response = null; try { request = new HttpRequestMessage(HttpMethod.Get, address); response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving an access token: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); return(OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."))); } var payload = JObject.FromObject(QueryHelpers.ParseQuery(await response.Content.ReadAsStringAsync()) .ToDictionary(pair => pair.Key, k => k.Value.ToString())); return(OAuthTokenResponse.Success(payload)); } finally { request?.Dispose(); response?.Dispose(); } }
protected override async Task <AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) { var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { throw new HttpRequestException($"An error occurred when retrieving Microsoft user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Microsoft Account API is enabled."); } using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); context.RunClaimActions(); await Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name)); }
/// <summary> /// Performs a backchannel request to obtain user information from the specified endpoint /// </summary> /// <param name="endpoint">Endpoint to return information from</param> /// <param name="accessToken">Bearer token for authentication</param> /// <param name="requestInformationType">Descriptive text of type of information request in event of exception</param> /// <returns>Parsed JSON document response from endpoint</returns> private async Task <JsonDocument> RequestUserInformationAsync(string endpoint, string accessToken, string requestInformationType) { using var request = new HttpRequestMessage(HttpMethod.Get, endpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError($"An error occurred while retrieving the {requestInformationType}: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync(Context.RequestAborted)); throw new HttpRequestException($"An error occurred while retrieving the {requestInformationType}."); } return(JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted))); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync([NotNull] string code, [NotNull] string redirectUri) { var request = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint) { Content = new FormUrlEncodedContent(new Dictionary <string, string> { ["client_id"] = Options.ClientId, ["redirect_uri"] = redirectUri, ["client_secret"] = Options.ClientSecret, ["code"] = code, ["grant_type"] = "authorization_code" }) }; var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving an access token: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); return(OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."))); } // Note: StackExchange's token endpoint doesn't return JSON but uses application/x-www-form-urlencoded. // Since OAuthTokenResponse expects a JSON payload, a response is manually created using the returned values. var content = QueryHelpers.ParseQuery(await response.Content.ReadAsStringAsync()); var payload = new JObject(); foreach (var item in content) { payload[item.Key] = (string)item.Value; } return(OAuthTokenResponse.Success(payload)); }
protected override async Task <AuthenticationTicket> CreateTicketAsync( ClaimsIdentity identity, Microsoft.AspNetCore.Authentication.AuthenticationProperties properties, OAuthTokenResponse tokens) { var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("Произошла ошибка при получении профиля пользователя: удаленный сервер " + "вернул {Status} ответ со следующей информацией: {Headers} {Body}.", response.StatusCode, response.Headers.ToString(), await response.Content.ReadAsStringAsync()); throw new HttpRequestException("Произошла ошибка при получении профиля пользователя."); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); identity.AddOptionalClaim(ClaimTypes.NameIdentifier, payload.Value <string>("id"), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Name, payload.Value <string>("login"), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Surname, payload.Value <string>("last_name"), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.GivenName, payload.Value <string>("first_name"), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Email, payload.Value <JArray>("emails")?[0]?.Value <string>(), Options.ClaimsIssuer); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload); context.RunClaimActions(); await Options.Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name)); }
protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { // Note: unlike the other social providers, the userinfo endpoint is user-specific and can't be set globally. // For more information, see https://developer.salesforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com var request = new HttpRequestMessage(HttpMethod.Get, tokens.Response.Value <string>("id")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred when retrieving the user profile: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); throw new HttpRequestException("An error occurred when retrieving the user from the Salesforce identity service."); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); identity.AddOptionalClaim(ClaimTypes.NameIdentifier, SalesforceAuthenticationHelper.GetUserIdentifier(payload), Options.ClaimsIssuer) .AddOptionalClaim(ClaimTypes.Name, SalesforceAuthenticationHelper.GetUserName(payload), Options.ClaimsIssuer) .AddOptionalClaim("urn:salesforce:email", SalesforceAuthenticationHelper.GetEmail(payload), Options.ClaimsIssuer) .AddOptionalClaim("urn:salesforce:thumbnail_photo", SalesforceAuthenticationHelper.GetThumbnailPhoto(payload), Options.ClaimsIssuer) .AddOptionalClaim("urn:salesforce:utc_offset", SalesforceAuthenticationHelper.GetUtcOffset(payload).ToString(), Options.ClaimsIssuer) .AddOptionalClaim("urn:salesforce:rest_url", SalesforceAuthenticationHelper.GetRestUrl(payload), Options.ClaimsIssuer); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme); var context = new OAuthCreatingTicketContext(ticket, Context, Options, Backchannel, tokens, payload); await Options.Events.CreatingTicket(context); return(context.Ticket); }
private async Task <(int ErrorCode, string?OpenId, string?UnionId)> GetUserIdentifierAsync(OAuthTokenResponse tokens) { // See https://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D for details var queryString = new Dictionary <string, string?>(3) { ["access_token"] = tokens.AccessToken, ["fmt"] = "json" // Return JSON instead of JSONP which is default due to historical reasons }; if (Options.ApplyForUnionId) { queryString.Add("unionid", "1"); } string address = QueryHelpers.AddQueryString(Options.UserIdentificationEndpoint, queryString); using var request = new HttpRequestMessage(HttpMethod.Get, address); using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { await Log.UserIdentifierErrorAsync(Logger, response, Context.RequestAborted); throw new HttpRequestException("An error occurred while retrieving the user identifier."); } using var stream = await response.Content.ReadAsStreamAsync(Context.RequestAborted); using JsonDocument payload = JsonDocument.Parse(stream); var payloadRoot = payload.RootElement; int errorCode = payloadRoot.TryGetProperty("error", out var errorCodeElement) && errorCodeElement.ValueKind == JsonValueKind.Number ? errorCodeElement.GetInt32() : 0; return(errorCode, payloadRoot.GetString("openid"), payloadRoot.GetString("unionid")); }
protected override async Task <AuthenticationTicket> CreateTicketAsync( [NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("bearer", tokens.AccessToken); // When a custom user agent is specified in the options, add it to the request headers // to override the default (generic) user agent used by the OAuth2 base middleware. if (!string.IsNullOrEmpty(Options.UserAgent)) { request.Headers.UserAgent.ParseAdd(Options.UserAgent); } using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving the user profile: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync(Context.RequestAborted)); throw new HttpRequestException("An error occurred while retrieving the user profile."); } using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); context.RunClaimActions(); await Options.Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name)); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context) { // for epic, must pass in list of scopes in tokenRequest form body, // AND they must match the permissions list in the Application Permissions // EXACTLY, or you get errors about how you don't have permission to view // the scope. Even if you're asking for LESS scope. var tokenRequestParameters = new Dictionary <string, string>() { { "redirect_uri", context.RedirectUri }, { "code", context.Code }, { "grant_type", "authorization_code" }, { "scopes", string.Join(" ", Options.Scope) } // <-- important! }; var requestContent = new FormUrlEncodedContent(tokenRequestParameters); var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // Epic requires passing in ClientId and ClientSecret via Authorization Header, // instead of in the form body. requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(Options.ClientId + ":" + Options.ClientSecret))); requestMessage.Content = requestContent; var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); if (response.IsSuccessStatusCode) { var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); return(OAuthTokenResponse.Success(payload)); } else { var error = "OAuth token endpoint failure: " + await Display(response); return(OAuthTokenResponse.Failed(new Exception(error))); } }
protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { var userId = tokens.Response.SelectToken("user").Value <string>("id"); var request = new HttpRequestMessage(HttpMethod.Get, $"{Options.UserInformationEndpoint}api/v1/users/{userId}/profile"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving the user profile: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); throw new HttpRequestException("An error occurred while retrieving the user from the Canvas identity service."); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var principal = new ClaimsPrincipal(identity); var roles = await GetAccountRolesForUserAsync(Backchannel, "1", userId, tokens.AccessToken); foreach (var role in roles) { identity.AddClaim(new Claim(ClaimTypes.Role, role)); } var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload); context.RunClaimActions(payload); await Options.Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name)); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri) { var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Options.ClientId}:{Options.ClientSecret}")); var request = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials); // When a custom user agent is specified in the options, add it to the request headers // to override the default (generic) user agent used by the OAuth2 base middleware. if (!string.IsNullOrEmpty(Options.UserAgent)) { request.Headers.UserAgent.ParseAdd(Options.UserAgent); } request.Content = new FormUrlEncodedContent(new Dictionary <string, string> { ["grant_type"] = "authorization_code", ["redirect_uri"] = redirectUri, ["code"] = code }); var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving an access token: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); return(OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."))); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); return(OAuthTokenResponse.Success(payload)); }
protected override async Task <OAuthTokenResponse> ExchangeCodeAsync([NotNull] string code, [NotNull] string redirectUri) { var request = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Content = new FormUrlEncodedContent(new Dictionary <string, string> { ["client_id"] = Options.ClientId, ["redirect_uri"] = redirectUri, ["client_secret"] = Options.ClientSecret, ["code"] = code, ["grant_type"] = "authorization_code" }); var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("An error occurred while retrieving an access token: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); return(OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."))); } // Note: Yammer doesn't return a standard OAuth2 response. To make this middleware compatible // with the OAuth2 generic middleware, a compliant JSON payload is generated manually. // See https://developer.yammer.com/docs/oauth-2 for more information about this process. var payload = JObject.Parse(await response.Content.ReadAsStringAsync())["access_token"].Value <JObject>(); payload["access_token"] = payload["token"]; payload["token_type"] = string.Empty; payload["refresh_token"] = string.Empty; payload["expires_in"] = string.Empty; return(OAuthTokenResponse.Success(payload)); }