internal RegistrationManager( IRegistrationContext registrationContext, IModuleManager moduleManager, IPublicRegistrationService publicRegistrationService, ISdkInformation sdkInformation, IEnvironmentInformation environmentInformation, IServiceContext serviceContext, ISecureRegistrationService secureRegistrationService, IConfigurationManager configurationManager, IEventBus eventBus, IRefreshToken tokenRefresher, ILogger logger, IJsonSerialiser serialiser) { _registrationContext = registrationContext; _moduleManager = moduleManager; _publicRegistrationService = publicRegistrationService; _sdkInformation = sdkInformation; _environmentInformation = environmentInformation; _serviceContext = serviceContext; _secureRegistrationService = secureRegistrationService; _configurationManager = configurationManager; _eventBus = eventBus; _tokenRefresher = tokenRefresher; _logger = logger; _serialiser = serialiser; }
/// <summary>Tests if this IRefreshToken is considered equal to another.</summary> /// <param name="other">The i refresh token to compare to this object.</param> /// <returns>true if the objects are considered equal, false if they are not.</returns> public bool Equals(IRefreshToken other) { if (this.ClientId == other.ClientId && this.RedirectUri == other.RedirectUri && this.Subject == other.Subject && this.ValidTo == other.ValidTo) { return true; } return false; }
/// <summary>Inserts the specified refresh token. Called when creating a refresh token.</summary> /// <param name="refreshToken">The refresh token.</param> /// <returns>The inserted refresh token. <c>null</c> if the insertion was unsuccessful.</returns> public async Task<IRefreshToken> InsertRefreshToken(IRefreshToken refreshToken) { var token = (SqlRefreshToken)refreshToken; using (var connection = this.OpenConnection()) { var id = await connection.QueryAsync<long>( "INSERT INTO RefreshTokens (ClientId, RedirectUri, Subject, Scope, Token, ValidTo, Created) VALUES (@ClientId, @RedirectUri, @Subject, @Scope, @Token, @ValidTo, @Created); SELECT CAST(SCOPE_IDENTITY() as bigint);", new { refreshToken.ClientId, refreshToken.RedirectUri, refreshToken.Subject, Scope = string.Join(" ", token.Scope), refreshToken.Token, refreshToken.ValidTo, Created = DateTime.UtcNow }); var data = await connection.QueryAsync("SELECT * FROM RefreshTokens WHERE Id = @Id", new { Id = id }); var entities = data.Select( x => new SqlRefreshToken() { ClientId = x.ClientId, Created = x.Created, Id = x.Id, RedirectUri = x.RedirectUri, Subject = x.Subject, Token = x.Token, ValidTo = x.ValidTo, Scope = x.Scope != null ? x.Scope.ToString().Split(' ') : new string[0] }); return entities.FirstOrDefault(); } }
/// <summary> /// Deletes the specified refresh token. Called when authenticating a refresh token to prevent re- /// use. /// </summary> /// <param name="refreshToken">The refresh token.</param> /// <returns><c>True</c> if successful, <c>false</c> otherwise.</returns> public async Task<bool> DeleteRefreshToken(IRefreshToken refreshToken) { var token = (SqlRefreshToken)refreshToken; using (var connection = this.OpenConnection()) { var rows = await connection.ExecuteAsync( "DELETE FROM RefreshTokens WHERE Id = @Id", new { Id = token.Id }); return rows == 1; } }
/// <summary>Inserts the specified refresh token.</summary> /// <param name="refreshToken">The refresh token.</param> /// <returns> /// The inserted refresh token. <c>null</c> if the insertion was unsuccessful. /// </returns> public async Task<IRefreshToken> InsertRefreshToken(IRefreshToken refreshToken) { var token = (RefreshToken)refreshToken; if (this.refreshTokens.TryAdd(Guid.NewGuid(), token)) { return refreshToken; } return null; }
/// <summary>Deletes the specified refresh token.</summary> /// <param name="refreshToken">The refresh token.</param> /// <returns><c>True</c> if successful, <c>false</c> otherwise.</returns> public async Task<bool> DeleteRefreshToken(IRefreshToken refreshToken) { var exists = this.refreshTokens.Any(x => x.Value.Equals(refreshToken)); if (exists) { var token = this.refreshTokens.First(x => x.Value.Equals(refreshToken)); RefreshToken removedToken; return this.refreshTokens.TryRemove(token.Key, out removedToken); } return false; }
/// <summary> /// Deletes the specified refresh token. Called when authenticating a refresh token to prevent re- /// use. /// </summary> /// <param name="refreshToken">The refresh token.</param> /// <returns><c>True</c> if successful, <c>false</c> otherwise.</returns> public async Task<bool> DeleteRefreshToken(IRefreshToken refreshToken) { var token = (RavenRefreshToken)refreshToken; using (var session = this.OpenAsyncSession()) { var match = await session.LoadAsync<RavenRefreshToken>(token.Id); session.Delete(match); await session.SaveChangesAsync(); return true; } }
public Task SaveRefreshToken(IRefreshToken token) { _refreshTokens.Add(token); return(Task.FromResult(token)); }
/// <summary>Inserts the specified refresh token. Called when creating a refresh token.</summary> /// <param name="refreshToken">The refresh token.</param> /// <returns>The inserted refresh token. <c>null</c> if the insertion was unsuccessful.</returns> public async Task<IRefreshToken> InsertRefreshToken(IRefreshToken refreshToken) { var token = (RavenRefreshToken)refreshToken; using (var session = this.OpenAsyncSession()) { await session.StoreAsync(token); await session.SaveChangesAsync(); return refreshToken; } }
public RefreshResponse(IRefreshToken refreshToken, IAudience audience) { this.RefreshToken = refreshToken; this.Audience = audience; }
/// <summary> /// Deletes the specified refresh token. Called when authenticating a refresh token to prevent re- /// use. /// </summary> /// <param name="refreshToken">The refresh token.</param> /// <returns><c>True</c> if successful, <c>false</c> otherwise.</returns> public async Task<bool> DeleteRefreshToken(IRefreshToken refreshToken) { var token = (RedisRefreshToken)refreshToken; var key = this.GenerateKey(token); var db = this.GetDatabase(); // Remove items from set // We don't need to remove the keys themselves, as Redis will remove them for us because we set the EXPIRE parameter. return await db.SortedSetRemoveAsync(this.Configuration.RefreshTokenPrefix, key); }
/// <summary>Inserts the specified refresh token. Called when creating a refresh token.</summary> /// <param name="refreshToken">The refresh token.</param> /// <returns>The inserted refresh token. <c>null</c> if the insertion was unsuccessful.</returns> public async Task<IRefreshToken> InsertRefreshToken(IRefreshToken refreshToken) { var token = (RedisRefreshToken)refreshToken; var key = this.GenerateKey(token); var db = this.GetDatabase(); try { if (token.ClientId == null || (token.RedirectUri == null && token.Scope == null) || token.Subject == null || token.Token == null || token.Created == DateTime.MinValue || token.ValidTo == DateTime.MinValue) { throw new ArgumentException(string.Format("The refresh token is invalid: {0}", JsonConvert.SerializeObject(token)), "refreshToken"); } this.Configuration.Log.DebugFormat("Inserting refresh token hash in key {0}", key); // Add hash to key await db.HashSetAsync(key, token.ToHashEntries()); var expires = refreshToken.ValidTo.ToUnixTime(); this.Configuration.Log.DebugFormat("Inserting key {0} to refresh token set with score {1}", key, expires); // Add key to sorted set for future reference. The score is the expire time in seconds since epoch. await db.SortedSetAddAsync(this.Configuration.RefreshTokenPrefix, key, expires); this.Configuration.Log.DebugFormat("Making key {0} expire at {1}", key, refreshToken.ValidTo); // Make the key expire when the code times out await db.KeyExpireAsync(key, refreshToken.ValidTo); return refreshToken; } catch (Exception ex) { this.Configuration.Log.Error("Error when inserting refresh token", ex); } return null; }
public void Authorize(HttpContext context, IDictionary <string, string> query) { string refreshToken; string clientId = ""; string clientSecret = ""; string username = ""; string userPassword = ""; string requestedScope = ""; //expected but already parsed input is: grant_type //expected input is: refresh token //expected input is (if enabled): client ID //expected input is (if enabled): client secret //expected input is (if enabled): user ID //expected input is (if enabled): user password //optional input is: scope //load refresh token if (query.ContainsKey("refresh_token")) { refreshToken = query["refresh_token"]; } else { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_REQUEST; errorResponse.Error_description = "The refresh token is missing. The request must include the query parameter 'refresh_token'"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.BadRequest; return; } //load client ID if (query.ContainsKey("client_id")) { clientId = query["client_id"]; } //load client secret if (query.ContainsKey("client_secret")) { clientSecret = query["client_secret"]; } //load user ID if (query.ContainsKey("username")) { username = query["username"]; } //load user password if (query.ContainsKey("password")) { userPassword = query["password"]; } //load scope if (query.ContainsKey("scope")) { requestedScope = query["scope"]; } //step 1: load refresh token and check whether the refresh token is listed and is valid IRefreshToken refreshTokenSet = _refreshStorage.GetRefreshToken(refreshToken); if (refreshTokenSet == null || refreshTokenSet.IsInvalidated) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The refresh token is invalid"; //TODO: add uri context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } //step 2: check whether the refresh token has expired if (refreshTokenSet.ValidUntil > 0 && refreshTokenSet.ValidUntil < DateTimeOffset.UtcNow.ToUnixTimeSeconds()) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The refresh token has expired"; //TODO: add uri context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } //step 3: load client and check whether the client is blocked IClientAccount client = _clientStorage.GetClient(refreshTokenSet.ClientId); if (client == null || client.IsBlocked) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The refresh token has been issued to a client which is not existing or has been blocked"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } //step 3a (if enabled): check whether client is linked to the refresh token if (client.IsClientIdRequiredForRefreshToken) { if (String.IsNullOrWhiteSpace(clientId)) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_REQUEST; errorResponse.Error_description = "The client ID is missing. The request must include the query parameter 'client_id'"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.BadRequest; return; } if (client.ClientId.CompareTo(clientId) != 0) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The client ID is invalid"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } } //step 3b (if enabled): check whether client secret is valid if (client.IsClientSecretRequiredForRefreshToken) { if (String.IsNullOrWhiteSpace(clientSecret)) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_REQUEST; errorResponse.Error_description = "The client secret is missing. The request must include the query parameter 'client_secret'"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.BadRequest; return; } if (client.ClientSecret.CompareTo(clientSecret) != 0) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The client ID or secret is invalid"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } } IUserAccount user = null; if (client.HasUser) { //step 4a: check whether the refresh token has been issued to a user if (String.IsNullOrWhiteSpace(refreshTokenSet.Subject)) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The refresh token is invalid"; //TODO: add uri context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } user = _userStorage.GetUserByName(refreshTokenSet.Subject); //step 4b: check whether the user is existing and not blocked if (user == null || user.IsBlocked) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The refresh token has been issued to a user which is not existing or has been blocked"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } //step 4c: check whether the linked subject (user ID) matches the passed user ID (if enabled) if (client.IsUserIdRequiredForRefreshToken) { if (String.IsNullOrWhiteSpace(username)) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_REQUEST; errorResponse.Error_description = "The username is missing. The request must include the query parameter 'username'"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.BadRequest; return; } if (refreshTokenSet.Subject.CompareTo(username) != 0) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The username is invalid"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } } //step 4d: check whether the passed user password is correct (if enabled) if (client.IsUserPasswordRequiredForRefreshToken) { if (String.IsNullOrWhiteSpace(userPassword)) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_REQUEST; errorResponse.Error_description = "The password is missing. The request must include the query parameter 'password'"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.BadRequest; return; } if (user.Password.CompareTo(userPassword) != 0) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The username or password is invalid"; //TODO: add URI context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } } } else { //check whether client has NO user, but the refresh token has been issued to a user: if (!String.IsNullOrWhiteSpace(refreshTokenSet.Subject)) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_CLIENT; errorResponse.Error_description = "The refresh token is invalid"; //TODO: add uri context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.Unauthorized; return; } } //step 4: check scope string finalScope = ""; if (String.IsNullOrWhiteSpace(requestedScope)) { finalScope = refreshTokenSet.Scope; } else { //extract single scopes from requested scope string[] scopes; if (requestedScope.Contains(",")) { scopes = requestedScope.Split(','); } else if (requestedScope.Contains(";")) { scopes = requestedScope.Split(';'); } else if (requestedScope.Contains(" ")) { scopes = requestedScope.Split(' '); } else { scopes = new string[] { requestedScope }; } //check whether all requested scopes are permitted: IList <string> permittedScope = refreshTokenSet.GetScopeAsList; foreach (string scope in scopes) { if (!permittedScope.Contains(scope)) { InvalidAuthorizationResponse errorResponse = new InvalidAuthorizationResponse(); errorResponse.Error = InvalidAuthorizationResponse.INVALID_SCOPE; errorResponse.Error_description = "The scope '" + scope + "' is not permitted or unknown"; //TODO: add URL context.Response.Payload.Write(JsonSerializer.SerializeJson(errorResponse)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Status = HttpStatus.BadRequest; return; } finalScope += scope + " "; } finalScope.Trim(); } //step 5: create new access token and issue new refresh token (if enabled and applicable) string subject; if (user == null) { subject = null; } else { subject = user.Username; } AccessToken accessToken = _reference.GenerateAccessToken(subject, clientId, finalScope, client.AccessTokenExpiryInSeconds); if (_issuesNewRefreshToken && refreshTokenSet.ValidUntil - DateTimeOffset.UtcNow.ToUnixTimeSeconds() < _newRefreshTokenExpiryThreshold) { accessToken.RefreshToken = _reference.GenerateRefreshToken(subject, client.ClientId, finalScope, client.RefreshTokenExpiryInSeconds); } else { accessToken.RefreshToken = null; } context.Response.Payload.Write(JsonSerializer.SerializeJson(accessToken)); context.Response.Headers.Set("Content-Type", MimeType.APPLICATION_JSON); context.Response.Headers.Set("Cache-Control", "no-store"); context.Response.Headers.Set("Pragma", "no-cache"); context.Response.Status = HttpStatus.OK; }