示例#1
0
        /// <summary>
        /// Deletes the access tokens that expires before the specified expire date. Called when creating
        /// an access token to cleanup.
        /// </summary>
        /// <param name="expires">The expire date.</param>
        /// <returns>The number of deleted tokens.</returns>
        public async Task <int> DeleteAccessTokens(DateTimeOffset expires)
        {
            var db   = this.GetDatabase();
            var tran = db.CreateTransaction();

            var keysToDelete = await db.SortedSetRangeByScoreAsync($"{this.Configuration.AccessTokenPrefix}:_index:expires", 0, expires.ToUnixTime());

            // Remove items from indexes
            tran.SortedSetRemoveRangeByScoreAsync($"{this.Configuration.AccessTokenPrefix}:_index:expires", 0, expires.ToUnixTime());

            // Remove items
            foreach (var key in keysToDelete)
            {
                var data = await db.HashGetAllAsync(key.ToString());

                var token = new RedisAccessToken(data);

                tran.HashDeleteAsync($"{this.Configuration.AccessTokenPrefix}:_index:client:{token.ClientId}", key);
                tran.HashDeleteAsync($"{this.Configuration.AccessTokenPrefix}:_index:redirecturi:{token.RedirectUri}", key);
                tran.HashDeleteAsync($"{this.Configuration.AccessTokenPrefix}:_index:subject:{token.Subject}", key);
                tran.KeyDeleteAsync(key.ToString());
            }

            var result = await tran.ExecuteAsync(CommandFlags.HighPriority);

            if (result)
            {
                return(keysToDelete.Length);
            }

            return(0);
        }
示例#2
0
        /// <summary>
        /// Gets all access tokens for the specified user that expires **after** the specified date.
        /// Called when authenticating an access token to limit the number of tokens to go through when validating the hash.
        /// </summary>
        /// <param name="subject">The subject.</param>
        /// <param name="expires">The expire date.</param>
        /// <returns>The access tokens.</returns>
        public async Task <IEnumerable <IAccessToken> > GetAccessTokens(string subject, DateTimeOffset expires)
        {
            var db = this.GetDatabase();

            var min    = expires.ToUnixTime();
            var tokens = new List <IAccessToken>();

            var nonExpiredKeys = await db.SortedSetRangeByScoreAsync($"{this.Configuration.AccessTokenPrefix}:_index:expires", min, DateTimeMax);

            var subjectKeys = await db.HashGetAllAsync($"{this.Configuration.AccessTokenPrefix}:_index:subject:{subject}");

            var unionKeys = nonExpiredKeys.Join(subjectKeys, x => x.ToString(), y => y.Name.ToString(), (x, y) => x);

            foreach (var key in unionKeys)
            {
                var hashEntries = await db.HashGetAllAsync(key.ToString());

                if (hashEntries.Any())
                {
                    var token = new RedisAccessToken(hashEntries);

                    if (token.ValidTo > expires)
                    {
                        tokens.Add(token);
                    }
                }
            }

            return(tokens.Where(x => x.Subject == subject));
        }
示例#3
0
        /// <summary>
        /// Gets all access tokens that expires **after** the specified date. Called when authenticating
        /// an access token to limit the number of tokens to go through when validating the hash.
        /// </summary>
        /// <param name="expires">The expire date.</param>
        /// <returns>The access tokens.</returns>
        public async Task <IEnumerable <IAccessToken> > GetAccessTokens(DateTimeOffset expires)
        {
            var db = this.GetDatabase();

            var min    = expires.ToUnixTime();
            var tokens = new List <IAccessToken>();

            var nonExpiredKeys = await db.SortedSetRangeByScoreAsync($"{this.Configuration.AccessTokenPrefix}:_index:expires", min, DateTimeMax);

            foreach (var key in nonExpiredKeys)
            {
                var hashEntries = await db.HashGetAllAsync(key.ToString());

                if (hashEntries.Any())
                {
                    var token = new RedisAccessToken(hashEntries);

                    if (token.ValidTo > expires)
                    {
                        tokens.Add(token);
                    }
                }
            }

            return(tokens);
        }
示例#4
0
        /// <summary>Inserts the specified access token. Called when creating an access token.</summary>
        /// <param name="accessToken">The access token.</param>
        /// <returns>The inserted access token. <c>null</c> if the insertion was unsuccessful.</returns>
        public async Task <IAccessToken> InsertAccessToken(IAccessToken accessToken)
        {
            var token = new RedisAccessToken(accessToken);

            // Validate token
            if (!token.IsValid())
            {
                throw new ArgumentException($"The access token is invalid: {JsonConvert.SerializeObject(token)}", nameof(accessToken));
            }

            var key = this.GenerateKeyPath(token);

            var db   = this.GetDatabase();
            var tran = db.CreateTransaction();

            try
            {
                this.Configuration.Log.DebugFormat("Inserting access token hash in key {0}", key);

                // Add hash to key
                tran.HashSetAsync(key, token.ToHashEntries());

                var expires = token.ValidTo.ToUnixTime();

                this.Configuration.Log.DebugFormat("Inserting key {0} to access token set with score {1}", key, expires);

                // Add key to sorted set for future reference by expire time. The score is the expire time in seconds since epoch.
                tran.SortedSetAddAsync($"{this.Configuration.AccessTokenPrefix}:_index:expires", key, expires);

                // Add key to hashed set for future reference by client id, redirect uri or subject. The value is the expire time in seconds since epoch.
                tran.HashSetAsync($"{this.Configuration.AccessTokenPrefix}:_index:client:{token.ClientId}", key, expires);
                tran.HashSetAsync($"{this.Configuration.AccessTokenPrefix}:_index:redirecturi:{token.RedirectUri}", key, expires);
                tran.HashSetAsync($"{this.Configuration.AccessTokenPrefix}:_index:subject:{token.Subject}", key, expires);

                this.Configuration.Log.DebugFormat("Making key {0} expire at {1}", key, token.ValidTo);

                // Make the keys expire when the code times out
                tran.KeyExpireAsync(key, token.ValidTo.UtcDateTime);

                await tran.ExecuteAsync();

                return(token);
            }
            catch (Exception ex)
            {
                this.Configuration.Log.Error("Error when inserting access token", ex);
            }

            return(null);
        }
示例#5
0
        /// <summary>Gets the specified access token.</summary>
        /// <param name="identifier">The identifier.</param>
        /// <returns>The access token.</returns>
        public async Task <IAccessToken> GetAccessToken(string identifier)
        {
            if (string.IsNullOrEmpty(identifier))
            {
                throw new ArgumentNullException(nameof(identifier));
            }

            var db  = this.GetDatabase();
            var key = Convert.ToBase64String(Encoding.UTF8.GetBytes(identifier));

            var hashEntries = await db.HashGetAllAsync($"{this.Configuration.AccessTokenPrefix}:{key}");

            if (hashEntries.Any())
            {
                var token = new RedisAccessToken(hashEntries);

                return(token);
            }

            return(null);
        }
示例#6
0
        /// <summary>
        /// Deletes the access tokens belonging to the specified client, redirect uri and subject.
        /// </summary>
        /// <param name="clientId">Identifier for the client.</param>
        /// <param name="redirectUri">The redirect uri.</param>
        /// <param name="subject">The subject.</param>
        /// <returns>The number of deleted tokens.</returns>
        public async Task <int> DeleteAccessTokens(string clientId, string redirectUri, string subject)
        {
            var db   = this.GetDatabase();
            var tran = db.CreateTransaction();

            var clientKeys      = db.HashGetAll($"{this.Configuration.AccessTokenPrefix}:_index:client:{clientId}");
            var redirectUriKeys = db.HashGetAll($"{this.Configuration.AccessTokenPrefix}:_index:redirecturi:{redirectUri}");
            var subjectKeys     = db.HashGetAll($"{this.Configuration.AccessTokenPrefix}:_index:subject:{subject}");

            var unionKeys = clientKeys
                            .Join(redirectUriKeys, x => x.Name, y => y.Name, (x, y) => x)
                            .Join(subjectKeys, x => x.Name, y => y.Name, (x, y) => x.Name);

            // Remove keys
            foreach (var key in unionKeys)
            {
                var data = await db.HashGetAllAsync(key.ToString());

                var token = new RedisAccessToken(data);

                // Remove items from indexes
                tran.SortedSetRemoveAsync($"{this.Configuration.AccessTokenPrefix}:_index:expires", key.ToString());
                tran.HashDeleteAsync($"{this.Configuration.AccessTokenPrefix}:_index:client:{token.ClientId}", key.ToString());
                tran.HashDeleteAsync($"{this.Configuration.AccessTokenPrefix}:_index:redirecturi:{token.RedirectUri}", key.ToString());
                tran.HashDeleteAsync($"{this.Configuration.AccessTokenPrefix}:_index:subject:{token.Subject}", key.ToString());

                // Remove key
                tran.KeyDeleteAsync(key.ToString());
            }

            var result = await tran.ExecuteAsync(CommandFlags.HighPriority);

            if (result)
            {
                return(unionKeys.Count());
            }

            return(0);
        }
示例#7
0
 /// <summary>Generates a key.</summary>
 /// <param name="accessToken">The access token.</param>
 /// <returns>The key.</returns>
 protected string GenerateKeyPath(RedisAccessToken accessToken)
 {
     return(this.Configuration.AccessTokenPrefix + ":" + Convert.ToBase64String(Encoding.UTF8.GetBytes(accessToken.GetIdentifier())));
 }