150 lines
5.2 KiB
Markdown
150 lines
5.2 KiB
Markdown
# Token Verification: App ID From Token vs Separate Parameter
|
|
|
|
## Current Implementation Analysis
|
|
|
|
### Current Flow (Requires app_id parameter):
|
|
```
|
|
1. Client: POST /api/verify {"token": "TESTT-abc123", "app_id": "test.example.com"}
|
|
2. System: Get app by app_id → Validate token against app's stored hashes
|
|
3. Result: Token validated within specific application scope
|
|
```
|
|
|
|
### Proposed Alternative (Extract app_id from token):
|
|
```
|
|
1. Client: POST /api/verify {"token": "TESTT-abc123"}
|
|
2. System: Extract prefix "TEST" → Find app by token_prefix → Validate token
|
|
3. Result: Token validated without requiring app_id parameter
|
|
```
|
|
|
|
## Security Analysis
|
|
|
|
### ✅ **Advantages of Token-Only Verification:**
|
|
|
|
#### 1. **Simpler API Usage**
|
|
```javascript
|
|
// Current (requires app knowledge)
|
|
await verify({token: "TESTT-abc123", app_id: "test.example.com"});
|
|
|
|
// Proposed (token-only)
|
|
await verify({token: "TESTT-abc123"});
|
|
```
|
|
|
|
#### 2. **Prevents App ID Mismatches**
|
|
- Current: Client could provide wrong app_id (though validation would fail)
|
|
- Proposed: App is determined directly from token - no mismatch possible
|
|
|
|
#### 3. **Better Token Portability**
|
|
- Tokens are self-describing and don't require external context
|
|
- Useful for tokens shared across different applications/services
|
|
|
|
### ❌ **Security Risks of Token-Only Verification:**
|
|
|
|
#### 1. **Information Disclosure Through Enumeration**
|
|
```bash
|
|
# Attacker can discover applications by trying token prefixes
|
|
curl -X POST /api/verify -d '{"token": "TESTX-fakehash"}'
|
|
# Response reveals if "TEST" application exists
|
|
|
|
curl -X POST /api/verify -d '{"token": "PRODX-fakehash"}'
|
|
# Response reveals if "PROD" application exists
|
|
```
|
|
|
|
#### 2. **Reduced Access Control Granularity**
|
|
```
|
|
Current: Client must know both token AND which app it belongs to
|
|
Proposed: Client only needs token - loses "need to know" app context
|
|
```
|
|
|
|
#### 3. **Prefix Collision Risk**
|
|
```sql
|
|
-- Multiple apps could theoretically have same prefix
|
|
INSERT INTO applications (app_id, token_prefix) VALUES
|
|
('app1.com', 'TEST'),
|
|
('app2.com', 'TEST'); -- Collision!
|
|
```
|
|
|
|
#### 4. **Weaker Defense Against Token Leakage**
|
|
- Current: Leaked token + need app_id knowledge = double barrier
|
|
- Proposed: Leaked token alone is sufficient = single barrier
|
|
|
|
## Implementation Feasibility
|
|
|
|
### Required Changes for Token-Only Verification:
|
|
|
|
```go
|
|
// 1. Add repository method
|
|
func (r *ApplicationRepository) GetByTokenPrefix(ctx context.Context, prefix string) (*domain.Application, error)
|
|
|
|
// 2. Extract prefix from token
|
|
func extractTokenPrefix(token string) string {
|
|
dashIndex := strings.Index(token, "-")
|
|
if dashIndex >= 3 && dashIndex <= 6 {
|
|
return token[:dashIndex-1] // Remove "T" or "UT" part
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// 3. Modified verification flow
|
|
func (s *tokenService) VerifyTokenOnly(ctx context.Context, token string) (*domain.VerifyResponse, error) {
|
|
prefix := extractTokenPrefix(token)
|
|
app, err := s.appRepo.GetByTokenPrefix(ctx, prefix)
|
|
if err != nil {
|
|
return &domain.VerifyResponse{Valid: false, Error: "Invalid token"}, nil
|
|
}
|
|
// Continue with existing verification logic...
|
|
}
|
|
```
|
|
|
|
### Database Schema Considerations:
|
|
```sql
|
|
-- Ensure unique prefixes (recommended)
|
|
ALTER TABLE applications ADD CONSTRAINT unique_token_prefix
|
|
UNIQUE (token_prefix) WHERE token_prefix != '';
|
|
|
|
-- Index already exists from migration 003
|
|
-- CREATE INDEX idx_applications_token_prefix ON applications(token_prefix);
|
|
```
|
|
|
|
## Recommendation: **Keep Current Implementation (Require app_id)**
|
|
|
|
### Reasoning:
|
|
|
|
#### **Security-First Approach** 🛡️
|
|
1. **Prevents enumeration attacks** - attackers can't discover apps by probing prefixes
|
|
2. **Maintains access control granularity** - clients must know token + app context
|
|
3. **Defense in depth** - two required pieces of information instead of one
|
|
4. **Clear audit trails** - logs show which app context was used for verification
|
|
|
|
#### **Architectural Benefits** 🏗️
|
|
1. **Explicit application scoping** - makes it clear which app owns the token
|
|
2. **Better error handling** - can distinguish between "invalid app" vs "invalid token"
|
|
3. **Supports multi-tenancy** - different apps can have isolated token validation
|
|
4. **Future extensibility** - can add per-app validation rules without breaking changes
|
|
|
|
#### **Operational Benefits** 🔧
|
|
1. **Clear API contracts** - consumers explicitly specify their context
|
|
2. **Better monitoring** - can track usage per application
|
|
3. **Simpler debugging** - logs clearly show app context for each verification
|
|
|
|
### **Optional: Provide Both Endpoints**
|
|
|
|
```go
|
|
// Existing secure endpoint (recommended for production)
|
|
POST /api/verify
|
|
{
|
|
"token": "TESTT-abc123",
|
|
"app_id": "test.example.com"
|
|
}
|
|
|
|
// Optional convenience endpoint (for development/testing)
|
|
POST /api/verify/token-only
|
|
{
|
|
"token": "TESTT-abc123"
|
|
}
|
|
```
|
|
|
|
## Final Answer
|
|
|
|
**Keep the current implementation requiring app_id** for security and architectural reasons. The slight inconvenience of requiring the app_id parameter provides significant security benefits and maintains better system architecture.
|
|
|
|
The token prefix provides tampering protection (which is working correctly), but requiring separate app_id provides additional security layers that outweigh the convenience of token-only verification. |