Authentication Overview
Clipron AI uses a robust authentication system combining JWT tokens with OAuth providers for secure access to the platform. This guide covers all authentication methods and security features.
Authentication Methods
JWT Tokens Primary authentication
Stateless authentication
Access and refresh tokens
Secure token signing
Google OAuth Social authentication
One-click login
Secure profile sync
No password management
GitHub OAuth Developer-focused
Repository access
Developer workflow integration
Seamless GitHub integration
JWT Token System
Token Types
Short-lived authentication token
Lifetime : 30 minutes (configurable)
Purpose : API access authorization
Storage : Memory or secure storage only
Refresh : Automatically refreshed by client
{
"sub" : "user_id_123" ,
"email" : "[email protected] " ,
"exp" : 1640995200 ,
"iat" : 1640993400 ,
"type" : "access"
}
Long-lived token renewal
Lifetime : 7 days (configurable)
Purpose : Generate new access tokens
Storage : Secure HTTP-only cookie
Rotation : New refresh token on each use
{
"sub" : "user_id_123" ,
"exp" : 1641600000 ,
"iat" : 1640993400 ,
"type" : "refresh" ,
"jti" : "unique_token_id"
}
Token Usage
API Requests
Token Refresh
Token Validation
Including tokens in requests # Header-based authentication (recommended)
curl -H "Authorization: Bearer <access_token>" \
https://clipron.com/api/user/profile
# Query parameter (not recommended for production)
curl "https://clipron.com/api/user/profile?token=<access_token>"
Automatic token renewal // Frontend token refresh logic
async function refreshToken () {
try {
const response = await fetch ( '/api/auth/refresh' , {
method: 'POST' ,
credentials: 'include' // Include refresh token cookie
});
if ( response . ok ) {
const { access_token } = await response . json ();
localStorage . setItem ( 'access_token' , access_token );
return access_token ;
}
} catch ( error ) {
// Redirect to login
window . location . href = '/login' ;
}
}
Server-side token verification from jose import JWTError, jwt
from datetime import datetime
def verify_token ( token : str ):
try :
payload = jwt.decode(
token,
SECRET_KEY ,
algorithms = [ ALGORITHM ]
)
# Check expiration
exp = payload.get( "exp" )
if exp and datetime.utcnow().timestamp() > exp:
raise JWTError( "Token expired" )
return payload
except JWTError:
raise HTTPException(
status_code = 401 ,
detail = "Invalid token"
)
OAuth Integration
Google OAuth Flow
Initiate OAuth
User clicks “Sign in with Google” button window . location . href = '/api/auth/google' ;
Google Authorization
User authorizes application on Google’s consent screen https://accounts.google.com/oauth/authorize?
client_id=your_client_id&
redirect_uri=https://clipron.com/auth/google/callback&
scope=openid email profile&
response_type=code
Authorization Code Exchange
Google redirects back with authorization code @app.get ( "/auth/google/callback" )
async def google_callback ( code : str ):
# Exchange code for tokens
token_response = await exchange_code_for_tokens(code)
user_info = await get_google_user_info(token_response.access_token)
# Create or update user
user = await create_or_update_user(user_info)
# Generate JWT tokens
access_token = create_access_token(user.id)
refresh_token = create_refresh_token(user.id)
return { "access_token" : access_token, "refresh_token" : refresh_token}
User Session Creation
Application creates user session with JWT tokens
GitHub OAuth Flow
GitHub Authorization
Similar to Google OAuth but with GitHub-specific scopes https://github.com/login/oauth/authorize?
client_id=your_github_client_id&
redirect_uri=https://clipron.com/auth/github/callback&
scope=user:email repo&
state=random_state_string
Repository Access
GitHub OAuth includes repository access permissions # After successful OAuth
github_token = oauth_response.access_token
# Store GitHub token for repository access
await store_github_token(user.id, github_token)
# Fetch user's repositories
repos = await fetch_github_repositories(github_token)
await sync_user_repositories(user.id, repos)
API Authentication
Bearer Token (Recommended)
Standard OAuth 2.0 Bearer token GET /api/user/profile HTTP / 1.1
Host : clipron.com
Authorization : Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type : application/json
For server-to-server communication GET /api/analysis HTTP / 1.1
Host : clipron.com
X-API-Key : clipron_api_key_your_key_here
Content-Type : application/json
Authentication Middleware
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
async def get_current_user (
credentials : HTTPAuthorizationCredentials = Depends(security)
):
"""Extract and validate user from JWT token"""
try :
payload = verify_token(credentials.credentials)
user_id = payload.get( "sub" )
if user_id is None :
raise HTTPException(
status_code = status. HTTP_401_UNAUTHORIZED ,
detail = "Invalid token"
)
user = await get_user_by_id(user_id)
if user is None :
raise HTTPException(
status_code = status. HTTP_401_UNAUTHORIZED ,
detail = "User not found"
)
return user
except JWTError:
raise HTTPException(
status_code = status. HTTP_401_UNAUTHORIZED ,
detail = "Invalid token"
)
# Usage in protected endpoints
@app.get ( "/api/user/profile" )
async def get_profile ( current_user : User = Depends(get_current_user)):
return current_user
Authorization and Permissions
Role-Based Access Control
User Roles
Permission Decorators
Resource Ownership
Different access levels class UserRole ( Enum ):
FREE = "free"
PRO = "pro"
ENTERPRISE = "enterprise"
ADMIN = "admin"
# Role-based permissions
ROLE_PERMISSIONS = {
UserRole. FREE : {
"analyses_per_month" : 50 ,
"private_repos" : False ,
"api_access" : False ,
"priority_support" : False
},
UserRole. PRO : {
"analyses_per_month" : 500 ,
"private_repos" : True ,
"api_access" : True ,
"priority_support" : True
},
UserRole. ENTERPRISE : {
"analyses_per_month" : - 1 , # Unlimited
"private_repos" : True ,
"api_access" : True ,
"priority_support" : True ,
"custom_integrations" : True
}
}
Endpoint protection def require_role ( required_role : UserRole):
def decorator ( func ):
async def wrapper ( * args , ** kwargs ):
current_user = kwargs.get( 'current_user' )
if not current_user or current_user.role.value < required_role.value:
raise HTTPException(
status_code = 403 ,
detail = "Insufficient permissions"
)
return await func( * args, ** kwargs)
return wrapper
return decorator
@app.post ( "/api/analysis/private-repo" )
@require_role (UserRole. PRO )
async def analyze_private_repo (
repo_data : dict ,
current_user : User = Depends(get_current_user)
):
# Only PRO and above can analyze private repos
pass
User-specific resource access async def verify_resource_ownership (
resource_id : str ,
current_user : User,
resource_type : str
):
"""Verify user owns the requested resource"""
if resource_type == "analysis" :
analysis = await get_analysis_by_id(resource_id)
if analysis.user_id != current_user.id:
raise HTTPException(
status_code = 403 ,
detail = "Access denied"
)
elif resource_type == "repository" :
repo = await get_repository_by_id(resource_id)
if repo.user_id != current_user.id:
raise HTTPException(
status_code = 403 ,
detail = "Access denied"
)
Security Features
Rate Limiting
Prevent abuse and ensure fair usage from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
limiter = Limiter( key_func = get_remote_address)
@app.post ( "/api/analysis" )
@limiter.limit ( "10/minute" ) # 10 analyses per minute
async def create_analysis (
request : Request,
analysis_data : dict ,
current_user : User = Depends(get_current_user)
):
# Rate limited endpoint
pass
Revoke compromised tokens # Store blacklisted tokens (in Redis or database)
blacklisted_tokens = set ()
def blacklist_token ( token : str ):
"""Add token to blacklist"""
blacklisted_tokens.add(token)
# Also store in persistent storage
redis_client.sadd( "blacklisted_tokens" , token)
def is_token_blacklisted ( token : str ) -> bool :
"""Check if token is blacklisted"""
return token in blacklisted_tokens or \
redis_client.sismember( "blacklisted_tokens" , token)
Track and manage user sessions class SessionManager :
def __init__ ( self ):
self .active_sessions = {}
async def create_session ( self , user_id : str , token : str ):
"""Create new user session"""
session_id = generate_session_id()
self .active_sessions[session_id] = {
"user_id" : user_id,
"token" : token,
"created_at" : datetime.utcnow(),
"last_activity" : datetime.utcnow()
}
return session_id
async def invalidate_user_sessions ( self , user_id : str ):
"""Invalidate all sessions for a user"""
sessions_to_remove = [
sid for sid, session in self .active_sessions.items()
if session[ "user_id" ] == user_id
]
for sid in sessions_to_remove:
del self .active_sessions[sid]
Error Handling
Authentication Errors
401 Unauthorized Missing or invalid token
Token expired
Invalid signature
Malformed token
Missing Authorization header
403 Forbidden Insufficient permissions
Role-based access denied
Resource ownership violation
Feature not available for user tier
{
"error" : {
"code" : "INVALID_TOKEN" ,
"message" : "The provided token is invalid or expired" ,
"details" : {
"reason" : "token_expired" ,
"expired_at" : "2024-01-01T12:00:00Z"
}
},
"request_id" : "req_123456789"
}
Security Tip : Always use HTTPS in production, store refresh tokens in secure HTTP-only cookies, and implement proper token rotation to maintain security.