/// <summary> /// Refreshes the OAuth 2.0 access token using the refresh token provided. /// </summary> /// <param name="pluginConfiguration">The configuration for the OAuth 2.0 identity provider.</param> /// <param name="refreshToken">The refresh token.</param> /// <param name="setHttpHeaders">If true, <see cref="AddAuthorizationHeader"/> will be called automatically.</param> /// <param name="token">A cancellation token for the task.</param> /// <returns>The updated access token data.</returns> public async Task <OAuth2TokenResponse> RefreshOAuth2TokenAsync( OAuth2Configuration pluginConfiguration, string refreshToken, bool setHttpHeaders = true, CancellationToken token = default(CancellationToken)) { // Sanity. if (null == pluginConfiguration) { throw new ArgumentNullException(nameof(pluginConfiguration)); } if (string.IsNullOrWhiteSpace(refreshToken)) { throw new ArgumentException("The OAuth 2.0 refresh token cannot be empty.", nameof(refreshToken)); } // Create the request, adding the mandatory items. var tokenEndpoint = new Uri(pluginConfiguration.TokenEndpoint, uriKind: UriKind.Absolute); var request = new RestSharp.RestRequest(tokenEndpoint.PathAndQuery, RestSharp.Method.POST); request.AddParameter("grant_type", "refresh_token"); request.AddParameter("refresh_token", refreshToken); request.AddParameter("redirect_uri", pluginConfiguration.GetAppropriateRedirectUri()); // Add the client id. If there's a realm then use that here too. request.AddParameter( "client_id", string.IsNullOrWhiteSpace(pluginConfiguration.SiteRealm) ? pluginConfiguration.ClientID // If no site realm is supplied, just pass the client ID. : $"{pluginConfiguration.ClientID}@{pluginConfiguration.SiteRealm}" // Otherwise pass client ID @ site realm. ); // Add the optional bits. request.AddParameterIfNotNullOrWhitespace("resource", pluginConfiguration.Resource); request.AddParameterIfNotNullOrWhitespace("scope", pluginConfiguration.Scope); request.AddParameterIfNotNullOrWhitespace("client_secret", pluginConfiguration.ClientSecret); // Make a post to the token endpoint. // NOTE: We must use a new RestClient here otherwise it'll try and add the token endpoint to the MFWA base url. var restClient = new RestSharp.RestClient(tokenEndpoint.GetLeftPart(UriPartial.Authority)); var response = await restClient.ExecutePostAsync <OAuth2TokenResponse>(request, token); // Validate response. if (null == response.Data || response.Data.TokenType != "Bearer") { throw new InvalidOperationException("OAuth token not received from endpoint, or token type was not bearer."); } // Set the authorisation header. if (setHttpHeaders) { this.AddAuthorizationHeader(pluginConfiguration, response.Data); } // Return the access token data. return(response.Data); }
/// <summary> /// Extracts data from the <paramref name="redirectUri"/> and uses it to retrieve access /// and refresh tokens from the provider. /// </summary> /// <param name="redirectUri">The Uri redirected to by the provider.</param> /// <returns>The token response, if /*available*/.</returns> private async Task <OAuth2TokenResponse> ProcessRedirectUri(Uri redirectUri) { // Does this represent an error? var queryParams = new UriBuilder(redirectUri).GetQueryParamsDictionary(); if (queryParams.ContainsKey("error")) { throw new InvalidOperationException ( $"Exception {queryParams["error"]} returned by authorisation endpoint." ); } // Check that the state was correct (not tampered with). if (this.oAuthPluginInfo.Configuration["state"]?.ToString() != queryParams["state"]) { throw new InvalidOperationException ( "The state returned by the authorisation endpoint was not correct." ); } // Retrieve the authorisation code from the URI. var code = queryParams.ContainsKey("code") ? queryParams["code"] : null; // Convert the authorisation code to tokens. // Create the request, adding the mandatory items. var tokenEndpoint = new Uri(this.oAuthPluginInfo.GetTokenEndpoint(), uriKind: UriKind.Absolute); var request = new RestSharp.RestRequest(tokenEndpoint.PathAndQuery, RestSharp.Method.POST); request.AddParameter("code", code); request.AddParameter("grant_type", "authorization_code"); request.AddParameter("redirect_uri", this.oAuthPluginInfo.GetAppropriateRedirectUri()); // Add the client id. If there's a realm then use that here too. { var siteRealm = this.oAuthPluginInfo.GetSiteRealm(); var clientId = this.oAuthPluginInfo.GetClientID(); request.AddParameter ( "client_id", string.IsNullOrWhiteSpace(siteRealm) ? clientId // If no site realm is supplied, just pass the client ID. : $"{clientId}@{siteRealm}" // Otherwise pass client ID @ site realm. ); } // Add the optional bits. request .AddParameterIfNotNullOrWhitespace("resource", this.oAuthPluginInfo.GetResource()) .AddParameterIfNotNullOrWhitespace("scope", this.oAuthPluginInfo.GetScope()) .AddParameterIfNotNullOrWhitespace("client_secret", this.oAuthPluginInfo.GetClientSecret()); // Execute the HTTP request. var restClient = new RestSharp.RestClient(tokenEndpoint.GetLeftPart(UriPartial.Authority)); var response = await restClient.ExecutePostAsync <OAuth2TokenResponse>(request); // Validate response. if (null == response.Data) { throw new InvalidOperationException("OAuth token not received from endpoint. Response: " + response.Content); } else if (response.Data.TokenType != "Bearer") { throw new InvalidOperationException("Token type was not bearer. Response: " + response.Content); } // Return the access token data. return(response.Data); }