public async Task <AccountRecoveryResult> RecoverAsync(string accountRecoveryPayload)
        {
            var payload = OwnIdSerializer.Deserialize <RecoveryPayload>(accountRecoveryPayload);

            if (string.IsNullOrEmpty(payload?.OobCode) || string.IsNullOrEmpty(payload.ApiKey))
            {
                throw new ArgumentException("Recovery code api key cannot be null", nameof(accountRecoveryPayload));
            }

            var url        = $"https://identitytoolkit.googleapis.com/v1/accounts:resetPassword?key={payload.ApiKey}";
            var httpResult = await _httpClient.PostAsJsonAsync(url,
                                                               new { oobCode = payload.OobCode, newPassword = _randomPasswordProvider.Generate() });

            var result =
                await httpResult.Content.ReadFromJsonAsync <RecoverPasswordResponse>(
                    OwnIdSerializer.GetDefaultProperties());

            if (!string.IsNullOrEmpty(result?.Error?.Message))
            {
                if (result.Error.Message == "EXPIRED_OOB_CODE")
                {
                    throw new OwnIdException(ErrorType.RecoveryTokenExpired);
                }

                throw new Exception($"Firebase REST -> {result.Error.Code}: {result.Error.Message}");
            }

            var user = await _firebaseContext.Auth.GetUserByEmailAsync(result !.Email);

            return(new AccountRecoveryResult
            {
                DID = user.Uid
            });
        }
예제 #2
0
        protected override async Task ExecuteAsync(HttpContext httpContext)
        {
            var request = await OwnIdSerializer.DeserializeAsync <ExchangeMagicLinkRequest>(httpContext.Request.Body);

            await JsonAsync(httpContext, await _exchangeMagicLinkCommand.ExecuteAsync(request),
                            StatusCodes.Status200OK);
        }
예제 #3
0
        protected override async Task ExecuteAsync(HttpContext context)
        {
            List <GetStatusRequest> request;

            try
            {
                request = await OwnIdSerializer.DeserializeAsync <List <GetStatusRequest> >(context.Request.Body);
            }
            catch
            {
                BadRequest(context.Response);
                return;
            }

            var result = await _getStatusCommand.ExecuteAsync(request);

            context.Response.StatusCode  = StatusCodes.Status200OK;
            context.Response.ContentType = "application/json";

            // TODO: remove after web ui sdk changes enums as strings
            await context.Response.WriteAsync(JsonSerializer.Serialize <object>(result, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                IgnoreNullValues     = true
            }));

            // TODO: uncomment after web ui sdk changes enums as strings
            // await Json(context, result, StatusCodes.Status200OK, false);
        }
 public IFormContext CreateUserDefinedContext(UserProfileData profileData,
                                              ILocalizationService localizationService)
 {
     return(new UserProfileFormContext <TProfile>(profileData.DID, profileData.PublicKey,
                                                  OwnIdSerializer.Deserialize <TProfile>(profileData.Profile?.GetRawText(), _serializerOptions),
                                                  localizationService));
 }
예제 #5
0
        public async Task InvokeAsync(HttpContext context)
        {
            using var reader = new StreamReader(context.Request.Body);
            var bodyString = await reader.ReadToEndAsync();

            if (string.IsNullOrEmpty(bodyString))
            {
                return;
            }

            var logMessage = OwnIdSerializer.Deserialize <LogMessage>(bodyString);

            using (LogContext.Push(new PropertyEnricher("source", logMessage.Source ?? "client-side")))
                using (LogContext.Push(new PropertyEnricher("version", logMessage.Version)))
                {
                    if (!Enum.TryParse(logMessage.LogLevel, true, out LogLevel logLevel))
                    {
                        logLevel = LogLevel.Debug;
                    }

                    using (_logger.BeginScope($"context: {context}", logMessage.Context))
                    {
                        _logger.LogWithData(logLevel, logMessage.Message, logMessage.Data);
                    }
                }
        }
        protected override async Task ExecuteAsync(HttpContext httpContext)
        {
            var request = await OwnIdSerializer.DeserializeAsync <ApproveActionRequest>(httpContext.Request.Body);

            await _approveActionCommand.ExecuteAsync(request);

            OkNoContent(httpContext.Response);
        }
예제 #7
0
        protected override async Task ExecuteAsync(HttpContext httpContext)
        {
            var request = await OwnIdSerializer.DeserializeAsync <GenerateContextRequest>(httpContext.Request.Body);

            var result = await _createFlowCommand.ExecuteAsync(request);

            await JsonAsync(httpContext, result, StatusCodes.Status200OK, false);
        }
예제 #8
0
        public async Task Invoke(HttpContext context)
        {
            var config = await OwnIdSerializer.DeserializeAsync <ConfigToInject>(context.Request.Body);

            _configuration.TFAEnabled            = config.TFAEnabled;
            _configuration.Fido2FallbackBehavior = config.Fido2FallbackBehavior;

            context.Response.StatusCode = StatusCodes.Status204NoContent;
        }
예제 #9
0
        public static IList <KeyValuePair <string, string> > AddParameter <T>(
            this IList <KeyValuePair <string, string> > nameValueCollection,
            string key,
            T value)
        {
            nameValueCollection.Add(
                new KeyValuePair <string, string>(key, OwnIdSerializer.Serialize(value)));

            return(nameValueCollection);
        }
예제 #10
0
        protected override async Task ExecuteAsync(HttpContext httpContext)
        {
            var request = await OwnIdSerializer.DeserializeAsync <AcceptStartRequest>(httpContext.Request.Body);

            var input = new TransitionInput <AcceptStartRequest>(RequestIdentity, GetRequestCulture(httpContext),
                                                                 request, ClientDate);

            var result = await _flowRunner.RunAsync(input, StepType.AcceptStart);

            await JsonAsync(httpContext, result, StatusCodes.Status200OK);
        }
예제 #11
0
        protected async Task <JwtContainer> GetRequestJwtContainerAsync(HttpContext httpContext)
        {
            var jwtContainer = await OwnIdSerializer.DeserializeAsync <JwtContainer>(httpContext.Request.Body);

            if (string.IsNullOrEmpty(jwtContainer?.Jwt))
            {
                throw new CommandValidationException("No JWT was found in request");
            }

            return(jwtContainer);
        }
        public async Task <CacheItem> ExecuteAsync(string fido2Payload, CacheItem relatedItem)
        {
            var request = OwnIdSerializer.Deserialize <Fido2LoginRequest>(fido2Payload);

            var storedFido2Info = await _userHandlerAdapter.FindFido2InfoAsync(request.CredentialId);

            if (storedFido2Info == null)
            {
                throw new OwnIdException(ErrorType.UserNotFound);
            }

            var options = new AssertionOptions
            {
                Challenge = Encoding.ASCII.GetBytes(relatedItem.Context),
                RpId      = _configuration.Fido2.RelyingPartyId
            };

            var fidoResponse = new AuthenticatorAssertionRawResponse
            {
                Extensions = new AuthenticationExtensionsClientOutputs(),
                Id         = Base64Url.Decode(request.CredentialId),
                RawId      = Base64Url.Decode(request.CredentialId),
                Response   = new AuthenticatorAssertionRawResponse.AssertionResponse
                {
                    AuthenticatorData = Base64Url.Decode(request.AuthenticatorData),
                    ClientDataJson    = Base64Url.Decode(request.ClientDataJson),
                    Signature         = Base64Url.Decode(request.Signature),
                    UserHandle        = Base64Url.Decode(storedFido2Info.UserId)
                },
                Type = PublicKeyCredentialType.PublicKey
            };

            var result = await _fido2.MakeAssertionAsync(
                fidoResponse,
                options,
                Base64Url.Decode(storedFido2Info.PublicKey),
                storedFido2Info.SignatureCounter,
                args =>
            {
                var storedCredentialId     = Base64Url.Decode(storedFido2Info.CredentialId);
                var storedCredDescriptor   = new PublicKeyCredentialDescriptor(storedCredentialId);
                var credIdValidationResult = storedCredDescriptor.Id.SequenceEqual(args.CredentialId);

                return(Task.FromResult(credIdValidationResult));
            });

            return(await _cacheItemRepository.UpdateAsync(relatedItem.Context, item =>
            {
                item.PublicKey = storedFido2Info.PublicKey;
                item.Fido2SignatureCounter = result.Counter;
                item.Fido2CredentialId = request.CredentialId;
                item.FinishFlow(storedFido2Info.UserId, storedFido2Info.PublicKey);
            }));
        }
        protected override async Task ExecuteAsync(HttpContext httpContext)
        {
            var request = await OwnIdSerializer.DeserializeAsync <UserIdentification>(httpContext.Request.Body);

            var commandInput = new TransitionInput <UserIdentification>(RequestIdentity, GetRequestCulture(httpContext),
                                                                        request, ClientDate);

            var result = await _flowRunner.RunAsync(commandInput, StepType.CheckUserExistence);

            await JsonAsync(httpContext, result, StatusCodes.Status200OK);
        }
        public async Task <CacheItem> ExecuteAsync(string fido2Payload, CacheItem relatedItem)
        {
            var request = OwnIdSerializer.Deserialize <Fido2RegisterRequest>(fido2Payload);

            if (string.IsNullOrWhiteSpace(request.AttestationObject))
            {
                throw new CommandValidationException("Incorrect Fido2 register request: AttestationObject is missing");
            }

            if (string.IsNullOrWhiteSpace(request.ClientDataJson))
            {
                throw new CommandValidationException("Incorrect Fido2 register request: ClientDataJson is missing");
            }

            var fido2Response = new AuthenticatorAttestationRawResponse
            {
                Id       = _encodingService.Base64UrlDecode(NewUserId),
                RawId    = _encodingService.Base64UrlDecode(NewUserId),
                Type     = PublicKeyCredentialType.PublicKey,
                Response = new AuthenticatorAttestationRawResponse.ResponseData
                {
                    AttestationObject = _encodingService.Base64UrlDecode(request.AttestationObject),
                    ClientDataJson    = _encodingService.Base64UrlDecode(request.ClientDataJson)
                }
            };

            var options = new CredentialCreateOptions
            {
                Challenge = _encodingService.ASCIIDecode(relatedItem.Context),
                Rp        = new PublicKeyCredentialRpEntity(
                    _configuration.Fido2.RelyingPartyId,
                    _configuration.Fido2.RelyingPartyName,
                    null),
                User = new Fido2User
                {
                    DisplayName = _configuration.Fido2.UserDisplayName,
                    Name        = _configuration.Fido2.UserName,
                    Id          = _encodingService.Base64UrlDecode(NewUserId)
                }
            };

            var result = await _fido2.MakeNewCredentialAsync(fido2Response, options, args => Task.FromResult(true));

            if (result == null)
            {
                throw new InternalLogicException("Cannot verify fido2 register request");
            }

            var publicKey = _encodingService.Base64UrlEncode(result.Result.PublicKey);

            return(await ProcessFido2RegisterResponseAsync(relatedItem, publicKey, result.Result.Counter,
                                                           _encodingService.Base64UrlEncode(result.Result.CredentialId)));
        }
        public async Task <CacheItem> GetAsync(string key)
        {
            var compKey = GetCustomerKey(key);
            var item    = await _redisDb.StringGetAsync(compKey);

            if (item.IsNullOrEmpty)
            {
                return(null);
            }

            _logger.Log(LogLevel.Debug, () => $"get(key -> '{compKey}') -> Response: '{item.ToString()}'");
            return(OwnIdSerializer.Deserialize <CacheItem>(item.ToString()));
        }
        public CacheItem Get(string key)
        {
            var compKey = GetCustomerKey(key);
            var item    = _redisDb.StringGet($"{_keyPrefix}{key}");

            if (item.IsNullOrEmpty)
            {
                return(null);
            }

            _logger.Log(LogLevel.Debug, () => $"get(key -> '{compKey}') -> Response: '{item.ToString()}'");
            return(OwnIdSerializer.Deserialize <CacheItem>(item.ToString()));
        }
예제 #17
0
        protected async Task JsonAsync <T>(HttpContext context, T responseBody, int statusCode, bool addLocaleHeader = true)
            where T : class
        {
            context.Response.StatusCode  = statusCode;
            context.Response.ContentType = "application/json";

            if (addLocaleHeader)
            {
                var culture = GetRequestCulture(context);
                context.Response.Headers.Add("Content-Language", culture.ToString());
            }

            await context.Response.WriteAsync(OwnIdSerializer.Serialize(responseBody));
        }
        public async Task SetAsync(string key, CacheItem data, TimeSpan expiration)
        {
            var serializedData = OwnIdSerializer.Serialize(data);
            var compKey        = GetCustomerKey(key);

            var isSuccess = await _redisDb.StringSetAsync(compKey, serializedData, expiration);

            _logger.Log(LogLevel.Debug, () => $"set(key -> '{compKey}' value -> '{serializedData}')");

            if (!isSuccess)
            {
                throw new Exception($"Can not set element to redis with context {data.Context}");
            }
        }
        public async Task <AccountRecoveryResult> RecoverAsync(string accountRecoveryPayload)
        {
            var payload     = OwnIdSerializer.Deserialize <GigyaAccountRecoveryPayload>(accountRecoveryPayload);
            var newPassword = _randomPasswordProvider.Generate();

            var resetPasswordResponse = await _apiClient.ResetPasswordAsync(payload.ResetToken, newPassword);

            if (resetPasswordResponse.ErrorCode != 0)
            {
                throw new OwnIdException(ErrorType.RecoveryTokenExpired, resetPasswordResponse.ErrorDetails);
            }

            return(new AccountRecoveryResult
            {
                DID = resetPasswordResponse.UID
            });
        }
        public async Task <LinkState> GetCurrentUserLinkStateAsync(string payload)
        {
            var jwt = OwnIdSerializer.Deserialize <JwtContainer>(payload)?.Jwt;

            if (string.IsNullOrEmpty(jwt))
            {
                throw new Exception("No JWT was found in HttpRequest");
            }

            var decodedToken = await _firebaseContext.Auth.VerifyIdTokenAsync(jwt);

            var did = decodedToken.Uid;

            var connections = await _firebaseContext.Db.Collection(Constants.CollectionName)
                              .WhereEqualTo(Constants.UserIdFieldName, did).GetSnapshotAsync();

            return(new LinkState(did, (uint)connections.Count));
        }
예제 #21
0
        protected override async Task ExecuteAsync(HttpContext httpContext)
        {
            // TODO: add parsing exception handling to BaseMiddleware
            AddConnectionRequest request;

            try
            {
                request = await OwnIdSerializer.DeserializeAsync <AddConnectionRequest>(httpContext.Request.Body);
            }
            catch
            {
                BadRequest(httpContext.Response);
                return;
            }

            var result = await _addConnectionCommand.ExecuteAsync(request);

            await JsonAsync(httpContext, result, StatusCodes.Status200OK);
        }
        public async Task InvokeAsync(HttpContext context)
        {
            if (string.Equals(context.Request.Path, "/ownid/log", StringComparison.InvariantCultureIgnoreCase))
            {
                await _next(context);

                return;
            }

            using (_logger.BeginScope(new { requestId = context.TraceIdentifier }))
                await using (var respStream = new MemoryStream())
                {
                    var originalRespStream = context.Response.Body;

                    try
                    {
                        context.Response.Body = respStream;
                        var body = string.Empty;

                        if (context.Request.Body.CanRead)
                        {
                            context.Request.EnableBuffering();
                            var stream = new StreamReader(context.Request.Body);
                            body = await stream.ReadToEndAsync();
                        }

                        var data = new
                        {
                            method = context.Request.Method,
                            scheme = context.Request.Scheme,
                            url    =
                                $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path.ToString()}{context.Request.QueryString.ToString()}",
                            body,
                            cookies = OwnIdSerializer.Serialize(context.Request.Cookies.ToDictionary(x => x.Key, x => x.Value)),
                            headers = OwnIdSerializer.Serialize(context.Request.Headers.ToDictionary(x => x.Key, x => x.Value))
                        };

                        _logger.LogWithData(LogLevel.Debug, "Request log", data);
                    }
                    catch (Exception e)
                    {
                        _logger.LogDebug(e, "failed to log request");
                    }
                    finally
                    {
                        try
                        {
                            context.Request.Body.Position = 0;
                        }
                        catch (Exception)
                        {
                        }
                    }

                    await _next(context);

                    try
                    {
                        var body = string.Empty;

                        respStream.Position = 0;
                        using var sr        = new StreamReader(respStream);
                        body = await sr.ReadToEndAsync();

                        respStream.Position = 0;

                        await respStream.CopyToAsync(originalRespStream);

                        context.Response.Body = originalRespStream;

                        var data = new
                        {
                            body,
                            statusCode = context.Response.StatusCode,
                            headers    = OwnIdSerializer.Serialize(context.Response.Headers.ToDictionary(x => x.Key, x => x.Value))
                        };

                        _logger.LogWithData(LogLevel.Debug, "Response  log", data);
                    }
                    catch (Exception e)
                    {
                        _logger.LogDebug(e, "failed to log request");
                    }
                }
        }
        public async Task <FrontendBehavior> ExecuteAsync(CacheItem cacheItem, string routingPayload)
        {
            if (!_coreConfiguration.Fido2.IsEnabled)
            {
                return(null);
            }

            // If this is second attempt to call same start endpoint
            if (cacheItem.FlowType == FlowType.Fido2Login ||
                cacheItem.FlowType == FlowType.Fido2Register ||
                cacheItem.FlowType == FlowType.Fido2Link ||
                cacheItem.FlowType == FlowType.Fido2LinkWithPin ||
                cacheItem.FlowType == FlowType.Fido2Recover ||
                cacheItem.FlowType == FlowType.Fido2RecoverWithPin)
            {
                return(null);
            }

            // Not supported for Fido2 flows
            if (cacheItem.FlowType != FlowType.PartialAuthorize &&
                cacheItem.FlowType != FlowType.Link &&
                cacheItem.FlowType != FlowType.Recover &&
                cacheItem.FlowType != FlowType.LinkWithPin &&
                cacheItem.FlowType != FlowType.RecoverWithPin)
            {
                return(null);
            }

            if (string.IsNullOrEmpty(routingPayload))
            {
                return(null);
            }

            var routing = OwnIdSerializer.Deserialize <ExtAuthenticationRouting>(routingPayload);

            if (routing.Authenticator != ExtAuthenticatorType.Fido2)
            {
                return(null);
            }

            if (!string.IsNullOrEmpty(routing.Error))
            {
                _logger.LogError($"Found error in FIDO2 ExtAuthenticationRouting object: {routing.Error}");
                return(null);
            }

            var initialFlowType      = cacheItem.FlowType;
            var initialChallengeType = cacheItem.ChallengeType;

            switch (routing.Type)
            {
            case "l":
            {
                if (cacheItem.FlowType == FlowType.PartialAuthorize)
                {
                    cacheItem.FlowType = FlowType.Fido2Login;
                }
                break;
            }

            case "r":
                cacheItem.FlowType = cacheItem.FlowType switch
                {
                    FlowType.PartialAuthorize => FlowType.Fido2Register,
                    FlowType.Link => FlowType.Fido2Link,
                    FlowType.LinkWithPin => FlowType.Fido2LinkWithPin,
                    FlowType.Recover => FlowType.Fido2Recover,
                    FlowType.RecoverWithPin => FlowType.Fido2RecoverWithPin,
                    _ => cacheItem.FlowType
                };

                if (cacheItem.FlowType == FlowType.Fido2Register)
                {
                    cacheItem.ChallengeType = ChallengeType.Register;
                }
                break;

            default:
                throw new InternalLogicException($"Incorrect fido2 request: '{routing.Type}'");
            }

            if (initialFlowType == cacheItem.FlowType)
            {
                return(null);
            }

            await _cacheItemRepository.UpdateAsync(cacheItem.Context, item =>
            {
                item.FlowType      = cacheItem.FlowType;
                item.ChallengeType = cacheItem.ChallengeType;
            });

            if (_eventsMetricsService != null && initialChallengeType != cacheItem.ChallengeType)
            {
                _eventsMetricsService?.LogSwitchAsync(initialChallengeType.ToEventType());
                _eventsMetricsService?.LogStartAsync(cacheItem.ChallengeType.ToEventType());
            }

            // TODO: rework to exclude explicit url creation
            return(new FrontendBehavior(StepType.Fido2Authorize, cacheItem.ChallengeType,
                                        new CallAction(_urlProvider.GetChallengeUrl(cacheItem.Context, ChallengeType.Register, "/fido2"))));
        }
예제 #24
0
        public async Task <LinkState> GetCurrentUserLinkStateAsync(string payload)
        {
            var jwt = OwnIdSerializer.Deserialize <JwtContainer>(payload)?.Jwt;

            if (string.IsNullOrEmpty(jwt))
            {
                throw new Exception("No JWT was found in HttpRequest");
            }

            var tokenHandler = new JwtSecurityTokenHandler();

            if (!tokenHandler.CanReadToken(jwt))
            {
                throw new Exception("Invalid jwt");
            }

            var rsaSecurityKey = await _restApiClient.GetPublicKey();

            try
            {
                tokenHandler.ValidateToken(jwt, new TokenValidationParameters
                {
                    IssuerSigningKey         = rsaSecurityKey,
                    RequireSignedTokens      = true,
                    RequireExpirationTime    = true,
                    ValidateIssuerSigningKey = true,
                    ValidateLifetime         = true,
                    ValidateAudience         = false,
                    ValidateIssuer           = false
                                               // TODO: add issuer to token for validation
                }, out _);
            }
            catch (SecurityTokenValidationException ex)
            {
                throw new Exception($"Token failed validation: {ex.Message}");
            }
            catch (ArgumentException ex)
            {
                throw new Exception($"Token was invalid: {ex.Message}");
            }

            var token = tokenHandler.ReadJwtToken(jwt);

            if (!token.Payload.ContainsKey(ApiKeyPayloadKey) ||
                (string)token.Payload[ApiKeyPayloadKey] != _configuration.ApiKey)
            {
                throw new Exception("Jwt was created for different apiKey");
            }

            var did = token.Subject;

            var accountInfo = await _restApiClient.SearchAsync <AccountInfoResponse <TProfile> >(GigyaFields.UID, did);

            if (!accountInfo.IsSuccess)
            {
                if (accountInfo.ErrorCode != 0)
                {
                    throw new Exception(accountInfo.GetFailureMessage());
                }

                throw new Exception($"Can't find user with did = {did}");
            }

            return(new LinkState(did, (uint)(accountInfo.First.Data?.OwnId?.Connections?.Count ?? 0)));
        }
 public UserHandlerAdapter(IUserHandler <TProfile> adaptee)
 {
     _adaptee           = adaptee;
     _serializerOptions = OwnIdSerializer.GetDefaultProperties();
     _serializerOptions.PropertyNamingPolicy = null;
 }