GetAPIKey retrieves an API key from the database using its key string. The provided key's prefix is used for efficient lookup, then bcrypt verifies the full key. Results are cached for 5 minutes to improve performance. This function is typically used for authentication and authorization purposes. P
(ctx context.Context, key string)
| 122 | // - *model.APIKey: The API key object if found (with hashed key, not plain text). |
| 123 | // - error: Returns ErrAPIKeyNotFound if the key doesn't exist, or other database errors if the query fails. |
| 124 | func (s *Datasource) GetAPIKey(ctx context.Context, key string) (*model.APIKey, error) { |
| 125 | // Extract prefix for efficient lookup |
| 126 | keyPrefix := getKeyPrefix(key) |
| 127 | cacheKey := getAPIKeyCacheKey(keyPrefix) |
| 128 | |
| 129 | // Try to get from cache first |
| 130 | var apiKey model.APIKey |
| 131 | if s.Cache != nil { |
| 132 | err := s.Cache.Get(ctx, cacheKey, &apiKey) |
| 133 | if err == nil && apiKey.Key != "" { |
| 134 | // Verify the cached key matches using bcrypt |
| 135 | if verifyAPIKey(apiKey.Key, key) { |
| 136 | return &apiKey, nil |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | // Cache miss - query the database using key_prefix for efficient lookup |
| 142 | query := ` |
| 143 | SELECT api_key_id, key, name, owner_id, scopes, expires_at, created_at, last_used_at, is_revoked, revoked_at |
| 144 | FROM ledgerforge.api_keys |
| 145 | WHERE key_prefix = $1 |
| 146 | ` |
| 147 | |
| 148 | rows, err := s.Conn.QueryContext(ctx, query, keyPrefix) |
| 149 | if err != nil { |
| 150 | return nil, err |
| 151 | } |
| 152 | defer func() { _ = rows.Close() }() |
| 153 | |
| 154 | // Check each matching key with bcrypt |
| 155 | for rows.Next() { |
| 156 | var candidate model.APIKey |
| 157 | var scopes pq.StringArray |
| 158 | err := rows.Scan( |
| 159 | &candidate.APIKeyID, |
| 160 | &candidate.Key, |
| 161 | &candidate.Name, |
| 162 | &candidate.OwnerID, |
| 163 | &scopes, |
| 164 | &candidate.ExpiresAt, |
| 165 | &candidate.CreatedAt, |
| 166 | &candidate.LastUsedAt, |
| 167 | &candidate.IsRevoked, |
| 168 | &candidate.RevokedAt, |
| 169 | ) |
| 170 | if err != nil { |
| 171 | return nil, err |
| 172 | } |
| 173 | candidate.Scopes = []string(scopes) |
| 174 | |
| 175 | // Verify the key using bcrypt |
| 176 | if verifyAPIKey(candidate.Key, key) { |
| 177 | // Cache the verified key |
| 178 | if s.Cache != nil { |
| 179 | err = s.Cache.Set(ctx, cacheKey, &candidate, 5*time.Minute) |
| 180 | if err != nil { |
| 181 | logrus.WithError(err).Warn("failed to cache API key") |