Beispiel #1
0
        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));
        }
Beispiel #2
0
        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));
        }
Beispiel #6
0
            /// <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);
            }
Beispiel #7
0
        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));
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        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);
        }