private async Task <ApiResponseType> PostDomesticPayment <ApiRequestType, ApiResponseType>( ApiRequestType payment, ApiProfile apiProfile, SoftwareStatementProfile softwareStatementProfile, BankClientProfile bankClientProfile, TokenEndpointResponse tokenEndpointResponse) where ApiRequestType : class where ApiResponseType : class { string payloadJson = JsonConvert.SerializeObject(payment); UriBuilder ub = new UriBuilder(new Uri(apiProfile.BaseUrl + "/domestic-payments")); List <HttpHeader> headers = CreateRequestHeaders( softwareStatement: softwareStatementProfile, payment: payment, client: bankClientProfile, tokenEndpointResponse: tokenEndpointResponse); return(await new HttpRequestBuilder() .SetMethod(HttpMethod.Post) .SetUri(ub.Uri) .SetHeaders(headers) .SetContentType("application/json") .SetContent(payloadJson) .Create() .RequestJsonAsync <ApiResponseType>(client: _apiClient, requestContentIsJson: true)); }
private static List <HttpHeader> CreateRequestHeaders <ApiRequestType>( SoftwareStatementProfile softwareStatement, ApiRequestType payment, BankClientProfile client, TokenEndpointResponse tokenEndpointResponse) where ApiRequestType : class { JwtFactory jwtFactory = new JwtFactory(); string jwt = jwtFactory.CreateJwt( profile: softwareStatement, claims: payment, useOpenBankingJwtHeaders: true); string[] jwsComponents = jwt.Split('.'); string jwsSig = $"{jwsComponents[0]}..{jwsComponents[2]}"; List <HttpHeader> headers = new List <HttpHeader> { new HttpHeader(name: "x-fapi-financial-id", value: client.XFapiFinancialId), new HttpHeader(name: "Authorization", value: "Bearer " + tokenEndpointResponse.AccessToken), new HttpHeader(name: "x-idempotency-key", value: Guid.NewGuid().ToString()), new HttpHeader(name: "x-jws-signature", value: jwsSig) }; return(headers); }
public async Task CreateAsync(AuthorisationCallbackDataPublic redirectData) { redirectData.ArgNotNull(nameof(redirectData)); // Load relevant data objects DomesticConsent consent = (await _domesticConsentRepo.GetAsync(dc => dc.State == redirectData.Response.State)) .FirstOrDefault() ?? throw new KeyNotFoundException( $"Consent with redirect state '{redirectData.Response.State}' not found."); ApiProfile apiProfile = await _apiProfileRepo.GetAsync(consent.ApiProfileId) ?? throw new KeyNotFoundException("API profile cannot be found."); BankClientProfile bankClientProfile = await _openBankingClientRepo.GetAsync(apiProfile.BankClientProfileId) ?? throw new KeyNotFoundException("Bank client profile cannot be found."); SoftwareStatementProfile softwareStatementProfile = _softwareStatementProfileService.GetSoftwareStatementProfile( bankClientProfile.SoftwareStatementProfileId); // Obtain token for consent string redirectUrl = softwareStatementProfile.DefaultFragmentRedirectUrl; TokenEndpointResponse tokenEndpointResponse = await PostAuthCodeGrant( authCode : redirectData.Response.Code, redirectUrl : redirectUrl, client : bankClientProfile); // Update consent with token consent.TokenEndpointResponse = tokenEndpointResponse; await _dbContextService.SaveChangesAsync(); }
/// <summary> /// Action invoked on being redirected from open identity provider. /// </summary> /// <returns>View after being redirected from open identity provider.</returns> /// <exception cref="System.NotSupportedException">Thrown when email claim does not exist.</exception> public ActionResult OAuthV2Redirect() { IdentityProviderClientConfigurationElement currentProvider = OpenIdConnectUtilities.GetCurrentProviderSettings(); // Check whether provider returned an error which could be a case if a user rejected a consent. string errorCode = this.HttpContext.Request.Params["error"]; if (errorCode != null) { string message = string.Format( CultureInfo.CurrentCulture, "The provider {0} returned error code {1} while processing user's credentials.", currentProvider.Name, errorCode); this.Response.Redirect("~", false); this.HttpContext.ApplicationInstance.CompleteRequest(); return(null); } string authorizationCode = OpenIdConnectUtilities.ValidateRequestAndGetAuthorizationCode(); if (authorizationCode == null) { string message = "Unable to find the authorization code for the login request."; SecurityException securityException = new SecurityException(message); throw securityException; } string bodyParameters = string.Format( CultureInfo.InvariantCulture, "grant_type=authorization_code&code={0}&redirect_uri={1}&client_id={2}&client_secret={3}", authorizationCode, currentProvider.RedirectUrl, currentProvider.ClientId, currentProvider.ClientSecret); OpenIdConnectConfiguration providerDiscoveryDocument = OpenIdConnectUtilities.GetDiscoveryDocument(currentProvider.Issuer); string returnValuesJson = OpenIdConnectUtilities.HttpPost(new Uri(providerDiscoveryDocument.TokenEndpoint), bodyParameters); TokenEndpointResponse tokenResponse = OpenIdConnectUtilities.DeserilizeJson <TokenEndpointResponse>(returnValuesJson); JwtSecurityToken token = OpenIdConnectUtilities.GetIdToken(tokenResponse.IdToken); Claim emailClaim = token.Claims.SingleOrDefault(c => string.Equals(c.Type, OpenIdConnectUtilities.Email, StringComparison.OrdinalIgnoreCase)); string email = null; // IdentityServer does not return email claim. if (emailClaim != null) { email = emailClaim.Value; } return(this.GetRedirectionBasedOnAssociatedCustomer(tokenResponse.IdToken, currentProvider.ProviderType, email)); }
public string GetOpenIdTokenEndpointResponseJson() { TokenEndpointResponse?model = new TokenEndpointResponse { AccessToken = GetAccessToken(), RefreshToken = "RefreshToken", ExpiresIn = 300, TokenType = "payments" }; return(JsonConvert.SerializeObject(model)); }
/// <summary> /// Action invoked on being redirected from open identity provider. /// </summary> /// <returns>View after being redirected from open identity provider.</returns> /// <exception cref="System.NotSupportedException">Thrown when email claim does not exist.</exception> public async Task <ActionResult> OAuthV2Redirect() { IdentityProviderClientConfigurationElement currentProvider = OpenIdConnectUtilities.GetCurrentProviderSettings(); // Check whether provider returned an error which could be a case if a user rejected a consent. string errorCode = ControllerContext.HttpContext.Request.Params["error"]; if (errorCode != null) { string message = string.Format( "The provider {0} returned error code {1} while processing user's credentials.", currentProvider.Name, errorCode); System.Diagnostics.Trace.TraceWarning(message); this.Response.Redirect("~", false); this.HttpContext.ApplicationInstance.CompleteRequest(); return(null); } string authorizationCode = OpenIdConnectUtilities.ValidateRequestAndGetAuthorizationCode(this.HttpContext); if (authorizationCode == null) { SecurityException securityException = new SecurityException("Unable to find the authorization code for the login request."); RetailLogger.Log.OnlineStoreAuthorizationCodeNotFoundForLogOnRequest(securityException); throw securityException; } string bodyParameters = string.Format( "grant_type=authorization_code&code={0}&redirect_uri={1}&client_id={2}&client_secret={3}", authorizationCode, currentProvider.RedirectUrl, currentProvider.ClientId, currentProvider.ClientSecret); OpenIdConnectConfiguration providerDiscoveryDocument = OpenIdConnectUtilities.GetDiscoveryDocument(currentProvider.Issuer); string returnValuesJson = OpenIdConnectUtilities.HttpPost(new Uri(providerDiscoveryDocument.TokenEndpoint), bodyParameters); TokenEndpointResponse tokenResponse = OpenIdConnectUtilities.DeserilizeJson <TokenEndpointResponse>(returnValuesJson); JwtSecurityToken token = OpenIdConnectUtilities.GetIdToken(tokenResponse.IdToken); Claim emailClaim = token.Claims.SingleOrDefault(c => string.Equals(c.Type, CookieConstants.Email, StringComparison.OrdinalIgnoreCase)); if (emailClaim == null) { RetailLogger.Log.OnlineStoreClaimNotFound(CookieConstants.Email, "Required for sign up using OpenIdAuth"); throw new SecurityException("Email claim does not exist."); } RedirectToRouteResult redirectResult = await this.GetRedirectionBasedOnAssociatedCustomer(this.HttpContext, tokenResponse.IdToken, currentProvider.ProviderType, emailClaim.Value); return(redirectResult); }
private async Task <ApiResponseType> PostDomesticConsent <ApiRequestType, ApiResponseType>( JwtFactory jwtFactory, SoftwareStatementProfile softwareStatementProfile, ApiRequestType consent, ApiProfile apiProfile, BankClientProfile bankClientProfile, TokenEndpointResponse tokenEndpointResponse) where ApiRequestType : class where ApiResponseType : class { string jwt = jwtFactory.CreateJwt( profile: softwareStatementProfile, claims: consent, useOpenBankingJwtHeaders: true); string[] jwsComponents = jwt.Split('.'); string jwsSignature = $"{jwsComponents[0]}..{jwsComponents[2]}"; UriBuilder ub = new UriBuilder(new Uri(apiProfile.BaseUrl + "/domestic-payment-consents")); string payloadJson = JsonConvert.SerializeObject(consent); List <HttpHeader> headers = new List <HttpHeader> { new HttpHeader(name: "x-fapi-financial-id", value: bankClientProfile.XFapiFinancialId), new HttpHeader(name: "Authorization", value: "Bearer " + tokenEndpointResponse.AccessToken), new HttpHeader(name: "x-idempotency-key", value: Guid.NewGuid().ToString()), new HttpHeader(name: "x-jws-signature", value: jwsSignature) }; return(await new HttpRequestBuilder() .SetMethod(HttpMethod.Post) .SetUri(ub.Uri) .SetHeaders(headers) .SetContentType("application/json") .SetContent(payloadJson) .Create() .RequestJsonAsync <ApiResponseType>(client: _apiClient, requestContentIsJson: true)); }
public async Task <OBWriteDomesticResponse4> CreateAsync(string consentId) { // Load relevant data objects DomesticConsent consent = await _domesticConsentRepo.GetAsync(consentId) ?? throw new KeyNotFoundException("The Consent does not exist."); ApiProfile apiProfile = await _apiProfileRepo.GetAsync(consent.ApiProfileId) ?? throw new KeyNotFoundException("The API Profile does not exist."); BankClientProfile bankClientProfile = await _openBankingClientRepo.GetAsync(apiProfile.BankClientProfileId) ?? throw new KeyNotFoundException( "The Bank Client Profile does not exist."); SoftwareStatementProfile softwareStatementProfile = _softwareStatementProfileService.GetSoftwareStatementProfile( bankClientProfile.SoftwareStatementProfileId); TokenEndpointResponse tokenEndpointResponse = _mapper.Map <TokenEndpointResponse>(consent.TokenEndpointResponse); // Create new Open Banking payment by posting JWT OBWriteDomesticConsent4 obConsent = consent.ObWriteDomesticConsent; OBWriteDomestic2 referencePayment = new OBWriteDomestic2 { Data = new OBWriteDomestic2Data { ConsentId = consent.BankId, Initiation = obConsent.Data.Initiation }, Risk = obConsent.Risk }; // Create new Open Banking payment by posting JWT OBWriteDomesticResponse4 paymentResponse; switch (apiProfile.ApiVersion) { case ApiVersion.V3P1P1: ObModels.PaymentInitiation.V3p1p1.Model.OBWriteDomestic2 newPayment = _mapper.Map <ObModels.PaymentInitiation.V3p1p1.Model.OBWriteDomestic2>(referencePayment); OBWriteDomesticResponse2 rawPaymentResponse = await PostDomesticPayment <ObModels.PaymentInitiation.V3p1p1.Model.OBWriteDomestic2, OBWriteDomesticResponse2>( payment : newPayment, apiProfile : apiProfile, softwareStatementProfile : softwareStatementProfile, bankClientProfile : bankClientProfile, tokenEndpointResponse : tokenEndpointResponse); paymentResponse = _mapper.Map <OBWriteDomesticResponse4>(rawPaymentResponse); break; case ApiVersion.V3P1P2: throw new ArgumentOutOfRangeException(); case ApiVersion.V3P1P4: paymentResponse = await PostDomesticPayment <OBWriteDomestic2, OBWriteDomesticResponse4>( payment : referencePayment, apiProfile : apiProfile, softwareStatementProfile : softwareStatementProfile, bankClientProfile : bankClientProfile, tokenEndpointResponse : tokenEndpointResponse); break; default: throw new ArgumentOutOfRangeException(); } return(paymentResponse); }
public async Task <PaymentConsentResponse> CreateAsync(DomesticPaymentConsent consent) { consent.ArgNotNull(nameof(consent)); // Load relevant objects ApiProfile apiProfile = await _apiProfileRepo.GetAsync(consent.ApiProfileId) ?? throw new KeyNotFoundException("The API Profile does not exist."); BankClientProfile bankClientProfile = await _bankClientProfileRepo.GetAsync(apiProfile.BankClientProfileId) ?? throw new KeyNotFoundException( "The Bank Client Profile does not exist."); SoftwareStatementProfile softwareStatementProfile = _softwareStatementProfileService.GetSoftwareStatementProfile( bankClientProfile.SoftwareStatementProfileId); // Get client credentials grant (we will not cache token for now but simply use to POST consent) TokenEndpointResponse tokenEndpointResponse = await PostClientCredentialsGrant(scope : "payments", client : bankClientProfile); // TODO: validate the response??? // Create new Open Banking consent by posting JWT JwtFactory jwtFactory = new JwtFactory(); OBWriteDomesticConsentResponse4 consentResponse; switch (apiProfile.ApiVersion) { case ApiVersion.V3P1P1: OBWriteDomesticConsent2 newDomesticConsent = _mapper.Map <OBWriteDomesticConsent2>(consent.DomesticConsent); OBWriteDomesticConsentResponse2 rawConsentResponse = await PostDomesticConsent <OBWriteDomesticConsent2, OBWriteDomesticConsentResponse2>( jwtFactory : jwtFactory, softwareStatementProfile : softwareStatementProfile, consent : newDomesticConsent, apiProfile : apiProfile, bankClientProfile : bankClientProfile, tokenEndpointResponse : tokenEndpointResponse); consentResponse = _mapper.Map <OBWriteDomesticConsentResponse4>(rawConsentResponse); break; case ApiVersion.V3P1P2: throw new ArgumentOutOfRangeException(); case ApiVersion.V3P1P4: consentResponse = await PostDomesticConsent <OBWriteDomesticConsent4, OBWriteDomesticConsentResponse4>( jwtFactory : jwtFactory, softwareStatementProfile : softwareStatementProfile, consent : consent.DomesticConsent, apiProfile : apiProfile, bankClientProfile : bankClientProfile, tokenEndpointResponse : tokenEndpointResponse); break; default: throw new ArgumentOutOfRangeException(); } // Generate URL for user auth string consentId = consentResponse.Data.ConsentId; string redirectUrl = softwareStatementProfile.DefaultFragmentRedirectUrl; if (redirectUrl == "") { redirectUrl = bankClientProfile.BankClientRegistrationClaims.RedirectUris[0]; } OAuth2RequestObjectClaims oAuth2RequestObjectClaims = Factories.CreateOAuth2RequestObjectClaims( openBankingClient: bankClientProfile, redirectUrl: redirectUrl, scope: new[] { "openid", "payments" }, intentId: consentId); string requestObjectJwt = jwtFactory.CreateJwt( profile: softwareStatementProfile, claims: oAuth2RequestObjectClaims, useOpenBankingJwtHeaders: false); Dictionary <string, string> keyValuePairs = new Dictionary <string, string> { { "response_type", oAuth2RequestObjectClaims.ResponseType }, { "client_id", oAuth2RequestObjectClaims.ClientId }, { "redirect_uri", oAuth2RequestObjectClaims.RedirectUri }, { "scope", oAuth2RequestObjectClaims.Scope }, { "request", requestObjectJwt }, { "nonce", oAuth2RequestObjectClaims.Nonce }, { "state", oAuth2RequestObjectClaims.State } }; string queryString = keyValuePairs.ToUrlEncoded(); string authUrl = bankClientProfile.OpenIdConfiguration.AuthorizationEndpoint + "?" + queryString; // Create and store persistent object string domesticConsentId = Guid.NewGuid().ToString(); DomesticConsent value = new DomesticConsent { State = oAuth2RequestObjectClaims.State, SoftwareStatementProfileId = bankClientProfile.SoftwareStatementProfileId, IssuerUrl = bankClientProfile.IssuerUrl, ApiProfileId = apiProfile.Id, ObWriteDomesticConsent = consent.DomesticConsent, TokenEndpointResponse = null, Id = domesticConsentId, BankId = consentId }; await _domesticConsentRepo.UpsertAsync(value); await _dbMultiEntityMethods.SaveChangesAsync(); return(new PaymentConsentResponse { AuthUrl = authUrl, ConsentId = domesticConsentId }); }
private async Task <TokenEndpointResponse> PostAuthCodeGrant( string authCode, string redirectUrl, BankClientProfile client) { UriBuilder ub = new UriBuilder(new Uri(client.OpenIdConfiguration.TokenEndpoint)); // Assemble URL-encoded form data string authHeader = null; Dictionary <string, string> keyValuePairs = new Dictionary <string, string> { { "grant_type", "authorization_code" }, { "redirect_uri", redirectUrl }, { "code", authCode } }; if (client.BankClientRegistrationClaims.TokenEndpointAuthMethod == "tls_client_auth") { keyValuePairs["client_id"] = client.BankClientRegistrationData.ClientId; } else if (client.BankClientRegistrationClaims.TokenEndpointAuthMethod == "client_secret_basic") { client.BankClientRegistrationData.ClientSecret.ArgNotNull("No client secret available."); string authString = client.BankClientRegistrationData.ClientId + ":" + client.BankClientRegistrationData.ClientSecret; byte[] plainTextBytes = Encoding.UTF8.GetBytes(authString); authHeader = "Basic " + Convert.ToBase64String(plainTextBytes); } else { if (client.BankClientRegistrationClaims.TokenEndpointAuthMethod == "tls_client_auth") { throw new InvalidOperationException("Found unsupported TokenEndpointAuthMethod"); } } string content = keyValuePairs.ToUrlEncoded(); // Assemble headers List <HttpHeader> headers = new List <HttpHeader> { new HttpHeader(name: "x-fapi-financial-id", value: client.XFapiFinancialId) }; if (authHeader != null) { headers.Add(new HttpHeader(name: "Authorization", value: authHeader)); } TokenEndpointResponse resp = await new HttpRequestBuilder() .SetMethod(HttpMethod.Post) .SetUri(ub.Uri) .SetHeaders(headers) .SetContentType("application/x-www-form-urlencoded") .SetContent(content) .Create() .RequestJsonAsync <TokenEndpointResponse>(client: _apiClient, requestContentIsJson: false); return(resp); }