/// <summary> /// Handles continuation of the process following a completed redirect, the request token url must be provided if it has been returned by the discovery process. /// Only the request and redirectedUrl are required, however if the redirect being handled is the result of calling the Authorization URL then the remaining parameters are required. /// </summary> /// <param name="request">Originating web request</param> /// <param name="redirectedUrl">Url redirected to by the completion of the previous step</param> /// <param name="sdkSession">SDKSession id used to fetch the discovery response with additional parameters that are required to request a token</param> /// <param name="expectedState">The state value returned from the StartAuthorization call should be passed here, it will be used to validate the authenticity of the authorization process</param> /// <param name="expectedNonce">The nonce value returned from the StartAuthorization call should be passed here, it will be used to ensure the token was not requested using a replay attack</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> HandleUrlRedirectAsync( HttpRequestMessage request, Uri redirectedUrl, string sdkSession = null, string expectedState = null, string expectedNonce = null, MobileConnectRequestOptions options = null, string version = null) { var discoveryResponse = await GetSessionFromCache(sdkSession); if (discoveryResponse == null && (expectedNonce != null || expectedState != null || sdkSession != null)) { return(GetCacheError()); } return(await CacheIfRequired( await MobileConnectInterfaceHelper.HandleUrlRedirect( _discovery, _authentication, _jwks, redirectedUrl, discoveryResponse, expectedState, expectedNonce, _config, options, version))); }
/// <summary> /// Performs headless authentication followed by request token if successful. Tokens will be validated before being returned. /// This may be a long running method as it waits for the authenticating user to respond using their authenticating device. /// </summary> /// <param name="request">Originating web request</param> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="encryptedMSISDN">Encrypted MSISDN/Subscriber Id returned from the Discovery process</param> /// <param name="state">Unique string to be used to prevent Cross Site Forgery Request attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="nonce">Unique string to be used to prevent replay attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="options">Optional parameters</param> /// <param name="cancellationToken">Cancellation token that can be used to cancel long running requests</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> RequestHeadlessAuthenticationAsync( HttpRequestMessage request, DiscoveryResponse discoveryResponse, string encryptedMSISDN, string state, string nonce, MobileConnectRequestOptions options, string version, CancellationToken cancellationToken = default(CancellationToken)) { state = string.IsNullOrEmpty(state) ? Security.GenerateSecureNonce() : state; nonce = string.IsNullOrEmpty(nonce) ? Security.GenerateSecureNonce() : nonce; return(await MobileConnectInterfaceHelper.RequestHeadlessAuthentication( _authentication, _jwks, _identity, discoveryResponse, encryptedMSISDN, state, nonce, _config, options, version, cancellationToken)); }
/// <summary> /// Request identity using the access token returned by <see cref="RequestTokenAsync(HttpRequestMessage, DiscoveryResponse, Uri, string, string, MobileConnectRequestOptions)"/> /// </summary> /// <param name="request">Originating web request</param> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="accessToken">Access token returned from RequestToken required to authenticate the request</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with requested Identity information</returns> public async Task <MobileConnectStatus> RequestPremiumInfoAsync( HttpRequestMessage request, DiscoveryResponse discoveryResponse, string accessToken, MobileConnectRequestOptions options) { return(await MobileConnectInterfaceHelper.RequestPremiumInfo( _identity, discoveryResponse, accessToken, _config, options)); }
/// <summary> /// Creates an authorization url with parameters to begin the authorization process /// </summary> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="encryptedMSISDN">Encrypted MSISDN/Subscriber Id returned from the Discovery process</param> /// <param name="state">Unique state value, this will be returned by the authorization process and should be checked for equality as a security measure</param> /// <param name="nonce">Unique value to associate a client session with an id token</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public MobileConnectStatus StartAuthentication( DiscoveryResponse discoveryResponse, string encryptedMSISDN, string state, string nonce, MobileConnectRequestOptions options, string version) { return(MobileConnectInterfaceHelper.StartAuthentication( _authentication, discoveryResponse, encryptedMSISDN, state, nonce, _config, options, version)); }
/// <summary> /// Performs headless authentication followed by request token if successful. Tokens will be validated before being returned. /// This may be a long running method as it waits for the authenticating user to respond using their authenticating device. /// </summary> /// <param name="request">Originating web request</param> /// <param name="sdkSession">SDKSession id used to fetch the discovery response with additional parameters that are required to generate the url</param> /// <param name="encryptedMSISDN">Encrypted MSISDN/Subscriber Id returned from the Discovery process</param> /// <param name="state">Unique string to be used to prevent Cross Site Forgery Request attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="nonce">Unique string to be used to prevent replay attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="options">Optional parameters</param> /// <param name="cancellationToken">Cancellation token that can be used to cancel long running requests</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> RequestHeadlessAuthenticationAsync(HttpRequestMessage request, string sdkSession, string encryptedMSISDN, string state, string nonce, MobileConnectRequestOptions options, CancellationToken cancellationToken = default(CancellationToken)) { var discoveryResponse = await GetSessionFromCache(sdkSession); if (discoveryResponse == null) { return(GetCacheError()); } return(await RequestHeadlessAuthenticationAsync(request, discoveryResponse, encryptedMSISDN, state, nonce, options, cancellationToken)); }
/// <summary> /// Request identity using the access token returned by <see cref="RequestTokenAsync(HttpRequestMessage, DiscoveryResponse, Uri, string, string, MobileConnectRequestOptions)"/> /// </summary> /// <param name="request">Originating web request</param> /// <param name="sdkSession">SDKSession id used to fetch the discovery response with additional parameters that are required to request a user info</param> /// <param name="accessToken">Access token returned from RequestToken required to authenticate the request</param> /// <param name="options">Additional optional parameters</param> /// <returns>MobileConnectStatus object with requested Identity information</returns> public async Task <MobileConnectStatus> RequestPremiumInfoAsync( HttpRequestMessage request, string sdkSession, string accessToken, MobileConnectRequestOptions options) { var discoveryResponse = await GetSessionFromCache(sdkSession); if (discoveryResponse == null) { return(GetCacheError()); } return(await RequestPremiumInfoAsync(request, discoveryResponse, accessToken, options)); }
/// <summary> /// Creates an authorization url with parameters to begin the authetication process /// </summary> /// <param name="request">Originating web request</param> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="encryptedMSISDN">Encrypted MSISDN/Subscriber Id returned from the Discovery process</param> /// <param name="state">Unique string to be used to prevent Cross Site Forgery Request attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="nonce">Unique string to be used to prevent replay attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public MobileConnectStatus StartAuthentication( HttpRequestMessage request, DiscoveryResponse discoveryResponse, string encryptedMSISDN, string state, string nonce, MobileConnectRequestOptions options, string version) { state = string.IsNullOrEmpty(state) ? Security.GenerateSecureNonce() : state; nonce = string.IsNullOrEmpty(nonce) ? Security.GenerateSecureNonce() : nonce; return(MobileConnectInterfaceHelper.StartAuthentication( _authentication, discoveryResponse, encryptedMSISDN, state, nonce, _config, options, version)); }
/// <summary> /// Attempt discovery using the supplied parameters. If msisdn, mcc and mnc are null the result /// will be operator selection, otherwise valid parameters will result in a StartAuthorization status /// </summary> /// <param name="request">Originating web request</param> /// <param name="msisdn">MSISDN from user</param> /// <param name="mcc">Mobile Country Code</param> /// <param name="mnc">Mobile Network Code</param> /// <param name="shouldProxyCookies">If cookies from the original request should be sent onto the discovery service</param> /// <param name="includeRequestIp">Including of remote ip address</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> AttemptDiscoveryAsync( HttpRequestMessage request, string msisdn, string mcc, string mnc, bool shouldProxyCookies, bool includeRequestIp, MobileConnectRequestOptions options) { var cookies = shouldProxyCookies ? request.GetCookies() : null; return(await CacheIfRequired( await MobileConnectInterfaceHelper.AttemptDiscovery( _discovery, msisdn, mcc, mnc, cookies, _config, options))); }
internal static async Task <MobileConnectStatus> AttemptDiscovery( IDiscoveryService discovery, string msisdn, string mcc, string mnc, IEnumerable <BasicKeyValuePair> cookies, MobileConnectConfig config, MobileConnectRequestOptions options) { DiscoveryResponse response = null; try { DiscoveryOptions discoveryOptions = options?.DiscoveryOptions ?? new DiscoveryOptions(); discoveryOptions.MSISDN = msisdn; discoveryOptions.IdentifiedMCC = mcc; discoveryOptions.IdentifiedMNC = mnc; discoveryOptions.RedirectUrl = config.RedirectUrl; discoveryOptions.XRedirect = config.XRedirect; response = await discovery.StartAutomatedOperatorDiscoveryAsync( config, config.RedirectUrl, discoveryOptions, cookies); } catch (MobileConnectInvalidArgumentException e) { Log.Error(() => $"An invalid argument was passed to AttemptDiscovery arg={e.Argument}"); return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (MobileConnectEndpointHttpException e) { Log.Error(() => $"A general http error occurred in AttemptDiscovery msisdn={!string.IsNullOrEmpty(msisdn)} mcc={mcc} mnc={mnc} discoveryUrl={config.DiscoveryUrl}"); return(MobileConnectStatus.Error(ErrorCodes.HttpFailure, "An HTTP failure occured while calling the discovery endpoint, the endpoint may be inaccessible", e)); } catch (Exception e) { Log.Error(() => $"A general error occurred in AttemptDiscovery msisdn={!string.IsNullOrEmpty(msisdn)} mcc={mcc} mnc={mnc} discoveryUrl={config.DiscoveryUrl}"); return(MobileConnectStatus.Error(ErrorCodes.Unknown, "An unknown error occured while calling the Discovery service to obtain operator details", e)); } return(GenerateStatusFromDiscoveryResponse(discovery, response)); }
/// <summary> /// Synchronous wrapper for <see cref="MobileConnectInterface.RequestTokenAsync(DiscoveryResponse, Uri, string, string, MobileConnectRequestOptions)"/> /// </summary> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="redirectedUrl">Uri redirected to by the completion of the authorization UI</param> /// <param name="expectedState">The state value returned from the StartAuthorization call should be passed here, it will be used to validate the authenticity of the authorization process</param> /// <param name="expectedNonce">The nonce value returned from the StartAuthorization call should be passed here, it will be used to ensure the token was not requested using a replay attack</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public MobileConnectStatus RequestToken( DiscoveryResponse discoveryResponse, Uri redirectedUrl, string expectedState, string expectedNonce, MobileConnectRequestOptions options, string version) { return(MobileConnectInterfaceHelper.RequestToken( _authentication, _jwks, discoveryResponse, redirectedUrl, expectedState, expectedNonce, _config, options, version).Result); }
/// <summary> /// Synchronous wrapper for <see cref="MobileConnectInterface.HandleUrlRedirectAsync(Uri, DiscoveryResponse, string, string, MobileConnectRequestOptions)"/> /// </summary> /// <param name="redirectedUrl">Url redirected to by the completion of the previous step</param> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="expectedState">The state value returned from the StartAuthorization call should be passed here, it will be used to validate the authenticity of the authorization process</param> /// <param name="expectedNonce">The nonce value returned from the StartAuthorization call should be passed here, it will be used to ensure the token was not requested using a replay attack</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public MobileConnectStatus HandleUrlRedirect( Uri redirectedUrl, DiscoveryResponse discoveryResponse = null, string expectedState = null, string expectedNonce = null, MobileConnectRequestOptions options = null, string version = null) { return(MobileConnectInterfaceHelper.HandleUrlRedirect( _discovery, _authentication, _jwks, redirectedUrl, discoveryResponse, expectedState, expectedNonce, _config, options, version).Result); }
/// <summary> /// Request token using the values returned from the authorization redirect /// </summary> /// <param name="request">Originating web request</param> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="redirectedUrl">Uri redirected to by the completion of the authorization UI</param> /// <param name="expectedState">The state value returned from the StartAuthorization call should be passed here, it will be used to validate the authenticity of the authorization process</param> /// <param name="expectedNonce">The nonce value returned from the StartAuthorization call should be passed here, it will be used to ensure the token was not requested using a replay attack</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> RequestTokenAsync( HttpRequestMessage request, DiscoveryResponse discoveryResponse, Uri redirectedUrl, string expectedState, string expectedNonce, MobileConnectRequestOptions options, string version) { return(await MobileConnectInterfaceHelper.RequestToken( _authentication, _jwks, discoveryResponse, redirectedUrl, expectedState, expectedNonce, _config, options, version)); }
/// <summary> /// Request token using the values returned from the authorization redirect /// </summary> /// <param name="request">Originating web request</param> /// <param name="sdkSession">SDKSession id used to fetch the discovery response with additional parameters that are required to request a token</param> /// <param name="redirectedUrl">Uri redirected to by the completion of the authorization UI</param> /// <param name="expectedState">The state value returned from the StartAuthorization call should be passed here, it will be used to validate the authenticity of the authorization process</param> /// <param name="expectedNonce">The nonce value returned from the StartAuthorization call should be passed here, it will be used to ensure the token was not requested using a replay attack</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> RequestTokenAsync( HttpRequestMessage request, string sdkSession, Uri redirectedUrl, string expectedState, string expectedNonce, MobileConnectRequestOptions options, string version) { var discoveryResponse = await GetSessionFromCache(sdkSession); if (discoveryResponse == null) { return(GetCacheError()); } return(await RequestTokenAsync( request, discoveryResponse, redirectedUrl, expectedState, expectedNonce, options, version)); }
/// <summary> /// Creates an authorization url with parameters to begin the authetication process, the SDKSession id is used to fetch the discovery response /// </summary> /// <param name="request">Originating web request</param> /// <param name="sdkSession">SDKSession id used to fetch the discovery response with additional parameters that are required to generate the url</param> /// <param name="encryptedMSISDN">Encrypted MSISDN/Subscriber Id returned from the Discovery process</param> /// <param name="state">Unique string to be used to prevent Cross Site Forgery Request attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="nonce">Unique string to be used to prevent replay attacks during request token process (defaults to guid if not supplied, value will be returned in MobileConnectStatus object)</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> StartAuthentication( HttpRequestMessage request, string sdkSession, string encryptedMSISDN, string state, string nonce, MobileConnectRequestOptions options, string version) { var discoveryResponse = await GetSessionFromCache(sdkSession); if (discoveryResponse == null) { return(GetCacheError()); } var authResult = StartAuthentication(request, discoveryResponse, encryptedMSISDN, state, nonce, options, version); return(authResult); }
/// <summary> /// Handles continuation of the process following a completed redirect, the request token url must be provided if it has been returned by the discovery process. /// Only the request and redirectedUrl are required, however if the redirect being handled is the result of calling the Authorization URL then the remaining parameters are required. /// </summary> /// <param name="request">Originating web request</param> /// <param name="redirectedUrl">Url redirected to by the completion of the previous step</param> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="expectedState">The state value returned from the StartAuthorization call should be passed here, it will be used to validate the authenticity of the authorization process</param> /// <param name="expectedNonce">The nonce value returned from the StartAuthorization call should be passed here, it will be used to ensure the token was not requested using a replay attack</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> HandleUrlRedirectAsync( HttpRequestMessage request, Uri redirectedUrl, DiscoveryResponse discoveryResponse = null, string expectedState = null, string expectedNonce = null, MobileConnectRequestOptions options = null, string version = null) { return(await CacheIfRequired( await MobileConnectInterfaceHelper.HandleUrlRedirect( _discovery, _authentication, _jwks, redirectedUrl, discoveryResponse, expectedState, expectedNonce, _config, options, version))); }
/// <summary> /// Request user info using the access token returned by <see cref="RequestTokenAsync(DiscoveryResponse, Uri, string, string, MobileConnectRequestOptions)"/> /// </summary> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="accessToken">Access token from RequestToken stage</param> /// <param name="options">Additional optional parameters</param> /// <returns>MobileConnectStatus object with UserInfo information</returns> public async Task <MobileConnectStatus> RequestIdentityAsync(DiscoveryResponse discoveryResponse, string accessToken, MobileConnectRequestOptions options) { return(await MobileConnectInterfaceHelper.RequestIdentity(_identity, discoveryResponse, accessToken, _config, options)); }
/// <summary> /// Synchronous wrapper for <see cref="MobileConnectInterface.AttemptDiscoveryAsync(string, string, string, MobileConnectRequestOptions)"/> /// </summary> /// <param name="msisdn">MSISDN from user</param> /// <param name="mcc">Mobile Country Code</param> /// <param name="mnc">Mobile Network Code</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public MobileConnectStatus AttemptDiscovery(string msisdn, string mcc, string mnc, MobileConnectRequestOptions options) { return(AttemptDiscoveryAsync(msisdn, mcc, mnc, options).Result); }
internal static MobileConnectStatus StartAuthentication(IAuthenticationService authentication, DiscoveryResponse discoveryResponse, string encryptedMSISDN, string state, string nonce, MobileConnectConfig config, MobileConnectRequestOptions options) { if (!IsUsableDiscoveryResponse(discoveryResponse)) { return(MobileConnectStatus.StartDiscovery()); } StartAuthenticationResponse response; try { string clientId = discoveryResponse.ResponseData.response.client_id ?? config.ClientId; string authorizationUrl = discoveryResponse.OperatorUrls.AuthorizationUrl; SupportedVersions supportedVersions = discoveryResponse.ProviderMetadata?.MobileConnectVersionSupported; AuthenticationOptions authOptions = options?.AuthenticationOptions ?? new AuthenticationOptions(); authOptions.ClientName = discoveryResponse.ApplicationShortName; response = authentication.StartAuthentication(clientId, authorizationUrl, config.RedirectUrl, state, nonce, encryptedMSISDN, supportedVersions, authOptions); } catch (MobileConnectInvalidArgumentException e) { Log.Error(() => $"An invalid argument was passed to StartAuthentication arg={e.Argument}"); return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (Exception e) { Log.Error(() => $"A general error occurred in AttemptDiscoveryAfterOperatorSelection state={state} nonce={nonce} authUrl={discoveryResponse.OperatorUrls.AuthorizationUrl}"); return(MobileConnectStatus.Error(ErrorCodes.Unknown, "An unknown error occured while generating an authorization url", e)); } return(MobileConnectStatus.Authentication(response.Url, state, nonce)); }
internal static async Task <MobileConnectStatus> RequestIdentity(IIdentityService _identity, DiscoveryResponse discoveryResponse, string accessToken, MobileConnectConfig _config, MobileConnectRequestOptions options) { string identityUrl = discoveryResponse?.OperatorUrls?.PremiumInfoUrl; var notSupported = IsSupported(identityUrl, "Identity", discoveryResponse?.ProviderMetadata?.Issuer); if (notSupported != null) { return(notSupported); } try { var response = await _identity.RequestIdentity(identityUrl, accessToken); return(MobileConnectStatus.Identity(response)); } catch (MobileConnectInvalidArgumentException e) { Log.Error(() => $"An invalid argument was passed to RequestIdentity arg={e.Argument}"); return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (MobileConnectEndpointHttpException e) { Log.Error(() => $"A general http error occurred in RequestUserInfo identityUrl={identityUrl}"); return(MobileConnectStatus.Error(ErrorCodes.HttpFailure, "An HTTP failure occured while calling the operator token endpoint, the endpoint may be inaccessible", e)); } catch (Exception e) { Log.Error(() => $"A general error occurred in RequestUserInfo identityUrl={identityUrl}"); return(MobileConnectStatus.Error(ErrorCodes.Unknown, "A failure occured while requesting a token", e)); } }
internal static async Task <MobileConnectStatus> RequestUserInfo(IIdentityService _identity, DiscoveryResponse discoveryResponse, string accessToken, MobileConnectConfig _config, MobileConnectRequestOptions options) { string userInfoUrl = discoveryResponse?.OperatorUrls?.UserInfoUrl; if (string.IsNullOrEmpty(userInfoUrl)) { Log.Error(() => $"UserInfo was not supported for issuer={discoveryResponse?.ProviderMetadata?.Issuer}"); return(MobileConnectStatus.Error(ErrorCodes.NotSupported, "UserInfo not supported with current operator", null)); } try { var response = await _identity.RequestUserInfo(userInfoUrl, accessToken); return(MobileConnectStatus.UserInfo(response)); } catch (MobileConnectInvalidArgumentException e) { Log.Error(() => $"An invalid argument was passed to RequestUserInfo arg={e.Argument}"); return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (MobileConnectEndpointHttpException e) { Log.Error(() => $"A general http error occurred in RequestUserInfo userInfoUrl={userInfoUrl}"); return(MobileConnectStatus.Error(ErrorCodes.HttpFailure, "An HTTP failure occured while calling the operator token endpoint, the endpoint may be inaccessible", e)); } catch (Exception e) { Log.Error(() => $"A general error occurred in RequestUserInfo userInfoUrl={userInfoUrl}"); return(MobileConnectStatus.Error(ErrorCodes.Unknown, "A failure occured while requesting a token", e)); } }
internal static async Task <MobileConnectStatus> HandleUrlRedirect(IDiscoveryService discovery, IAuthenticationService authentication, IJWKeysetService jwks, Uri redirectedUrl, DiscoveryResponse discoveryResponse, string expectedState, string expectedNonce, MobileConnectConfig config, MobileConnectRequestOptions options) { if (HttpUtils.ExtractQueryValue(redirectedUrl.Query, "code") != null) { return(await RequestToken(authentication, jwks, discoveryResponse, redirectedUrl, expectedState, expectedNonce, config, options)); } else if (HttpUtils.ExtractQueryValue(redirectedUrl.Query, "mcc_mnc") != null) { return(await AttemptDiscoveryAfterOperatorSelection(discovery, redirectedUrl, config)); } string errorCode = HttpUtils.ExtractQueryValue(redirectedUrl.Query, "error") ?? ErrorCodes.InvalidRedirect; string errorDesc = HttpUtils.ExtractQueryValue(redirectedUrl.Query, "error_description") ?? HttpUtils.ExtractQueryValue(redirectedUrl.Query, "description") ?? string.Format("Unable to parse next step using {0}", redirectedUrl.AbsoluteUri); return(MobileConnectStatus.Error(errorCode, errorDesc, null)); }
private static MobileConnectStatus HandleTokenResponse(IAuthenticationService authentication, RequestTokenResponse response, string clientId, string issuer, string expectedNonce, string version, JWKeyset jwks, MobileConnectRequestOptions options) { if (response.ErrorResponse != null) { return(MobileConnectStatus.Error(response.ErrorResponse.Error, response.ErrorResponse.ErrorDescription, null, response)); } response.ValidationResult = authentication.ValidateTokenResponse(response, clientId, issuer, expectedNonce, options?.MaxAge, jwks, version); var validationOptions = options?.TokenValidationOptions ?? new TokenValidationOptions(); if (!validationOptions.AcceptedValidationResults.HasFlag(response.ValidationResult)) { Log.Error(() => $"A generated tokenResponse was invalid issuer={issuer} version={version} result={response.ValidationResult}"); return(MobileConnectStatus.Error(ErrorCodes.InvalidToken, $"The token was found to be invalid with the validation result {response.ValidationResult}", null, response)); } else if (response.ValidationResult != TokenValidationResult.Valid) { Log.Warning(() => $"A generated tokenResponse was invalid but accepted issuer={issuer} version={version} result={response.ValidationResult}"); } return(MobileConnectStatus.Complete(response)); }
internal static async Task <MobileConnectStatus> AttemptDiscovery(IDiscovery discovery, string msisdn, string mcc, string mnc, IEnumerable <BasicKeyValuePair> cookies, MobileConnectConfig config, MobileConnectRequestOptions options) { DiscoveryResponse response = null; try { DiscoveryOptions discoveryOptions = options?.DiscoveryOptions ?? new DiscoveryOptions(); discoveryOptions.MSISDN = msisdn; discoveryOptions.IdentifiedMCC = mcc; discoveryOptions.IdentifiedMNC = mnc; discoveryOptions.RedirectUrl = config.RedirectUrl; response = await discovery.StartAutomatedOperatorDiscoveryAsync(config, config.RedirectUrl, discoveryOptions, cookies); } catch (MobileConnectInvalidArgumentException e) { return(MobileConnectStatus.Error("invalid_argument", string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (MobileConnectEndpointHttpException e) { return(MobileConnectStatus.Error("http_failure", "An HTTP failure occured while calling the discovery endpoint, the endpoint may be inaccessible", e)); } catch (Exception e) { return(MobileConnectStatus.Error("unknown_error", "An unknown error occured while calling the Discovery service to obtain operator details", e)); } return(GenerateStatusFromDiscoveryResponse(discovery, response)); }
/// <summary> /// Syncronous wrapper for <see cref="RequestPremiumInfoAsync"/> /// </summary> /// <param name="discoveryResponse">The response returned by the discovery process</param> /// <param name="accessToken">Access token from RequestToken stage</param> /// <param name="options">Additional optional parameters</param> /// <returns>MobileConnectStatus object with UserInfo information</returns> public MobileConnectStatus RequestPremiumInfo( DiscoveryResponse discoveryResponse, string accessToken, MobileConnectRequestOptions options) { return(MobileConnectInterfaceHelper.RequestPremiumInfo( _identity, discoveryResponse, accessToken, _config, options).Result); }
/// <summary> /// Attempt discovery using the supplied parameters. If msisdn, mcc and mnc are null the result will be /// operator selection, otherwise valid parameters will result in a StartAuthorization status /// </summary> /// <param name="msisdn">MSISDN from user</param> /// <param name="mcc">Mobile Country Code</param> /// <param name="mnc">Mobile Network Code</param> /// <param name="options">Optional parameters</param> /// <returns>MobileConnectStatus object with required information for continuing the mobileconnect process</returns> public async Task <MobileConnectStatus> AttemptDiscoveryAsync( string msisdn, string mcc, string mnc, MobileConnectRequestOptions options) { return(await MobileConnectInterfaceHelper.AttemptDiscovery( _discovery, msisdn, mcc, mnc, null, _config, options)); }
internal static async Task <MobileConnectStatus> RequestHeadlessAuthentication(IAuthenticationService authentication, IJWKeysetService jwks, IIdentityService identity, DiscoveryResponse discoveryResponse, string encryptedMSISDN, string state, string nonce, MobileConnectConfig config, MobileConnectRequestOptions options, CancellationToken cancellationToken = default(CancellationToken)) { if (!IsUsableDiscoveryResponse(discoveryResponse)) { return(MobileConnectStatus.StartDiscovery()); } MobileConnectStatus status; try { string clientId = discoveryResponse.ResponseData.response.client_id ?? config.ClientId; string clientSecret = discoveryResponse.ResponseData.response.client_secret; string authorizationUrl = discoveryResponse.OperatorUrls.AuthorizationUrl; string tokenUrl = discoveryResponse.OperatorUrls.RequestTokenUrl; string issuer = discoveryResponse.ProviderMetadata.Issuer; SupportedVersions supportedVersions = discoveryResponse.ProviderMetadata.MobileConnectVersionSupported; AuthenticationOptions authOptions = options?.AuthenticationOptions ?? new AuthenticationOptions(); authOptions.ClientName = discoveryResponse.ApplicationShortName; var jwksTask = jwks.RetrieveJWKSAsync(discoveryResponse.OperatorUrls.JWKSUrl); var tokenTask = authentication.RequestHeadlessAuthentication(clientId, clientSecret, authorizationUrl, tokenUrl, config.RedirectUrl, state, nonce, encryptedMSISDN, supportedVersions, authOptions, cancellationToken); // execute both tasks in parallel await Task.WhenAll(tokenTask, jwksTask).ConfigureAwait(false); RequestTokenResponse response = tokenTask.Result; status = HandleTokenResponse(authentication, response, clientId, issuer, nonce, discoveryResponse.ProviderMetadata.MobileConnectVersionSupported.MaxSupportedVersionString, jwksTask.Result, options); } catch (MobileConnectInvalidArgumentException e) { Log.Error(() => $"An invalid argument was passed to RequestHeadlessAuthentication arg={e.Argument}"); return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (MobileConnectEndpointHttpException e) { Log.Error(() => $"A general http error occurred in RequestHeadlessAuthentication state={state} nonce={nonce} authUrl={discoveryResponse.OperatorUrls.AuthorizationUrl} tokenUrl={discoveryResponse.OperatorUrls.RequestTokenUrl}"); return(MobileConnectStatus.Error(ErrorCodes.HttpFailure, "An HTTP failure occured while calling the discovery endpoint, the endpoint may be inaccessible", e)); } catch (Exception e) { Log.Error(() => $"A general error occurred in RequestHeadlessAuthentication state={state} nonce={nonce} authUrl={discoveryResponse.OperatorUrls.AuthorizationUrl} tokenUrl={discoveryResponse.OperatorUrls.RequestTokenUrl}"); return(MobileConnectStatus.Error(ErrorCodes.Unknown, "An unknown error occured while generating an authorization url", e)); } if (status.ResponseType == MobileConnectResponseType.Error || !options.AutoRetrieveIdentityHeadless || string.IsNullOrEmpty(discoveryResponse.OperatorUrls.PremiumInfoUrl)) { return(status); } var identityStatus = await RequestIdentity(identity, discoveryResponse, status.TokenResponse.ResponseData.AccessToken, config, options); status.IdentityResponse = identityStatus.IdentityResponse; return(status); }
internal static MobileConnectStatus StartAuthorization(IAuthentication authentication, DiscoveryResponse discoveryResponse, string encryptedMSISDN, string state, string nonce, MobileConnectConfig config, MobileConnectRequestOptions options) { StartAuthenticationResponse response; try { string clientId = discoveryResponse?.ResponseData?.response?.client_id ?? config.ClientId; string authorizationUrl = discoveryResponse?.OperatorUrls?.AuthorizationUrl; response = authentication.StartAuthentication(clientId, authorizationUrl, config.RedirectUrl, state, nonce, config.AuthorizationScope, config.AuthorizationMaxAge, config.AuthorizationAcrValues, encryptedMSISDN, options?.AuthenticationOptions); } catch (MobileConnectInvalidArgumentException e) { return(MobileConnectStatus.Error("invalid_argument", string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (Exception e) { return(MobileConnectStatus.Error("unknown_error", "An unknown error occured while generating an authorization url", e)); } return(MobileConnectStatus.Authorization(response.Url, state, nonce)); }
internal static async Task <MobileConnectStatus> RequestToken(IAuthenticationService authentication, IJWKeysetService jwks, DiscoveryResponse discoveryResponse, Uri redirectedUrl, string expectedState, string expectedNonce, MobileConnectConfig config, MobileConnectRequestOptions options) { RequestTokenResponse response; if (!IsUsableDiscoveryResponse(discoveryResponse)) { return(MobileConnectStatus.StartDiscovery()); } if (string.IsNullOrEmpty(expectedState)) { return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, "ExpectedState argument was not supplied, this is needed to prevent Cross-Site Request Forgery", null)); } if (string.IsNullOrEmpty(expectedNonce)) { return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, "expectedNonce argument was not supplied, this is needed to prevent Replay Attacks", null)); } var actualState = HttpUtils.ExtractQueryValue(redirectedUrl.Query, "state"); if (expectedState != actualState) { return(MobileConnectStatus.Error(ErrorCodes.InvalidState, "State values do not match, this could suggest an attempted Cross-Site Request Forgery", null)); } try { var code = HttpUtils.ExtractQueryValue(redirectedUrl.Query, "code"); var clientId = discoveryResponse.ResponseData.response.client_id ?? config.ClientId; var clientSecret = discoveryResponse.ResponseData.response.client_secret ?? config.ClientSecret; var requestTokenUrl = discoveryResponse.OperatorUrls.RequestTokenUrl; var issuer = discoveryResponse.ProviderMetadata.Issuer; var tokenTask = authentication.RequestTokenAsync(clientId, clientSecret, requestTokenUrl, config.RedirectUrl, code); var jwksTask = jwks.RetrieveJWKSAsync(discoveryResponse.OperatorUrls.JWKSUrl); // execute both tasks in parallel await Task.WhenAll(tokenTask, jwksTask).ConfigureAwait(false); response = tokenTask.Result; var maxSupportedVersion = discoveryResponse.ProviderMetadata?.MobileConnectVersionSupported == null ? "mc_v1.1" : discoveryResponse.ProviderMetadata.MobileConnectVersionSupported.MaxSupportedVersionString; return(HandleTokenResponse(authentication, response, clientId, issuer, expectedNonce, maxSupportedVersion, jwksTask.Result, options)); } catch (MobileConnectInvalidArgumentException e) { Log.Error(() => $"An invalid argument was passed to RequestToken arg={e.Argument}"); return(MobileConnectStatus.Error(ErrorCodes.InvalidArgument, string.Format("An argument was found to be invalid during the process. The argument was {0}.", e.Argument), e)); } catch (MobileConnectEndpointHttpException e) { Log.Error(() => $"A general http error occurred in RequestToken redirectedUrl={redirectedUrl} requestTokenUrl={discoveryResponse.OperatorUrls.RequestTokenUrl} jwksUrl={discoveryResponse.OperatorUrls.JWKSUrl}"); return(MobileConnectStatus.Error(ErrorCodes.HttpFailure, "An HTTP failure occured while calling the operator token endpoint, the endpoint may be inaccessible", e)); } catch (Exception e) { Log.Error(() => $"A general error occurred in RequestToken redirectedUrl={redirectedUrl} requestTokenUrl={discoveryResponse.OperatorUrls.RequestTokenUrl} jwksUrl={discoveryResponse.OperatorUrls.JWKSUrl}"); return(MobileConnectStatus.Error(ErrorCodes.Unknown, "A failure occured while requesting a token", e)); } }