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
            });
        }
 public IFormContext CreateUserDefinedContext(UserProfileData profileData,
                                              ILocalizationService localizationService)
 {
     return(new UserProfileFormContext <TProfile>(profileData.DID, profileData.PublicKey,
                                                  OwnIdSerializer.Deserialize <TProfile>(profileData.Profile?.GetRawText(), _serializerOptions),
                                                  localizationService));
 }
Пример #3
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);
                    }
                }
        }
        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);
            }));
        }
        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()));
        }
        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));
        }
Пример #10
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 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"))));
        }