Documentation Index Fetch the complete documentation index at: https://mintlify.com/Arvo-AI/aurora/llms.txt
Use this file to discover all available pages before exploring further.
OAuth Flow
Aurora uses OAuth 2.0 for secure authentication with cloud providers. Each provider implements OAuth differently.
Supported OAuth Providers
Google Cloud Platform OAuth 2.0 with consent screen
Tailscale OAuth client credentials flow
OVH Cloud OAuth 2.0 with custom scopes
Microsoft Azure Service principal (client credentials)
GCP uses standard OAuth 2.0 authorization code flow.
Flow Diagram
1. Initiate OAuth Flow
POST /gcp/login
Response:
{
"login_url" : "https://accounts.google.com/o/oauth2/v2/auth?client_id=AURORA_CLIENT_ID&redirect_uri=https://aurora.example.com/gcp/callback&response_type=code&scope=https://www.googleapis.com/auth/cloud-platform&state=user_123&access_type=offline&prompt=consent"
}
2. User Consent
Redirect user to login_url:
User authenticates with Google
Reviews requested permissions
Grants consent
Google redirects to Aurora callback
3. Callback & Token Exchange
GET /gcp/callback
Google redirects with:
code - Authorization code
state - User ID (for security)
Aurora:
Validates state parameter
Exchanges code for tokens
Stores tokens in Vault
Triggers post-auth setup
Redirects to frontend
Redirect URL:
https://aurora.example.com/chat?login=gcp_setup_pending&task_id=abc123
4. Post-Auth Setup
Asynchronous setup tasks:
Fetch user’s GCP projects
Enable required APIs
Create service accounts
Configure IAM permissions
Set up billing exports
Initialize project metadata
Complete setup
Poll Status:
GET /gcp/setup/status/abc123
Progress Response:
{
"state" : "PROGRESS" ,
"status" : "Enabling Cloud Resource Manager API" ,
"complete" : false ,
"progress" : 57 ,
"step" : 4 ,
"total_steps" : 7
}
Complete Response:
{
"state" : "SUCCESS" ,
"status" : "Setup completed" ,
"complete" : true ,
"result" : {
"projects_configured" : 3 ,
"apis_enabled" : 15
}
}
OAuth Scopes
GCP OAuth requires:
https://www.googleapis.com/auth/cloud-platform - Full cloud platform access
https://www.googleapis.com/auth/compute - Compute Engine
https://www.googleapis.com/auth/cloudresourcemanager - Project management
Token Management
Access Token - Short-lived (1 hour), used for API calls
Refresh Token - Long-lived, used to get new access tokens
Expiration - Stored in database (expires_at field)
Refresh - Automatic when access token expires
Tailscale OAuth
Tailscale uses OAuth 2.0 client credentials flow.
Flow Diagram
Client Credentials Flow
POST /tailscale_api/tailscale/connect
{
"clientId" : "oauth-client-123" ,
"clientSecret" : "tskey-client-secret" ,
"tailnet" : "example.com"
}
Aurora:
Requests access token from Tailscale
Validates token by fetching devices
Stores credentials in Vault
Generates SSH key pair
Creates reusable auth key
Response:
{
"success" : true ,
"message" : "Tailscale connected successfully" ,
"tailnet" : "example.com" ,
"tailnetName" : "Example Org" ,
"deviceCount" : 12
}
Token Refresh
POST /tailscale_api/tailscale/refresh-token
Manually refresh access token:
{
"success" : true ,
"message" : "Token refreshed successfully" ,
"expiresAt" : "2024-03-15T12:00:00Z"
}
OAuth Scopes
Tailscale scopes:
devices - Read device information
devices:write - Manage devices
acl - Read ACL configuration
acl:write - Modify ACL
keys - Manage auth keys
Azure Service Principal
Azure uses service principal authentication (similar to OAuth client credentials).
Flow Diagram
Service Principal Authentication
POST /azure/login
{
"userId" : "user_123" ,
"tenantId" : "tenant-guid" ,
"clientId" : "app-guid" ,
"clientSecret" : "secret-value" ,
"subscriptionId" : "sub-guid"
}
Aurora:
Creates ClientSecretCredential
Requests management token
Validates by fetching subscriptions
Stores credentials in Vault
Response:
{
"message" : "Successfully logged in to Azure" ,
"subscription_id" : "sub-guid" ,
"subscription_name" : "Production Subscription"
}
Token Expiration
Access tokens expire after 1 hour
Automatically refreshed on API calls
Uses stored service principal credentials
No manual refresh required
OAuth Security
State Parameter
Prevents CSRF attacks:
// Encode user_id in state
const state = encodeURIComponent ( userId );
const authUrl = ` ${ OAUTH_URL } ?state= ${ state } ` ;
// Validate on callback
const userId = decodeURIComponent ( request . query . state );
if ( ! userId ) {
throw new Error ( 'Invalid state parameter' );
}
External ID (AWS)
Adds security layer for role assumption:
{
"Version" : "2012-10-17" ,
"Statement" : [
{
"Effect" : "Allow" ,
"Principal" : {
"AWS" : "arn:aws:iam::AURORA_ACCOUNT:root"
},
"Action" : "sts:AssumeRole" ,
"Condition" : {
"StringEquals" : {
"sts:ExternalId" : "UNIQUE_EXTERNAL_ID"
}
}
}
]
}
Each workspace has unique external ID
Prevents confused deputy problem
Required for role assumption
Token Storage
HashiCorp Vault:
# Store tokens
vault_client.write(
f 'aurora/users/ { user_id } /gcp' ,
{
'access_token' : access_token,
'refresh_token' : refresh_token,
'expires_at' : expires_at
}
)
# Database stores reference
db.execute(
"INSERT INTO user_tokens (user_id, provider, secret_ref) VALUES ( %s , %s , %s )" ,
(user_id, 'gcp' , f 'vault:kv/data/aurora/users/ { user_id } /gcp' )
)
Credential Rotation
GCP:
Access tokens expire after 1 hour
Refresh tokens used to get new access tokens
Refresh tokens rotated on use (optional)
AWS:
STS credentials expire after 1 hour (default)
Automatically re-assumed on expiration
External ID never changes
Azure:
Access tokens expire after 1 hour
Client secrets expire after configured period
No automatic rotation (manual renewal required)
Tailscale:
Access tokens expire after 90 days (default)
Manual refresh via /tailscale/refresh-token
Client credentials never expire
Error Handling
OAuth Errors
Invalid Grant:
{
"error" : "invalid_grant" ,
"error_description" : "Authorization code expired"
}
Action: Restart OAuth flow
Access Denied:
{
"error" : "access_denied" ,
"error_description" : "User denied consent"
}
Action: Inform user, allow retry
Invalid Client:
{
"error" : "invalid_client" ,
"error_description" : "Invalid client credentials"
}
Action: Verify Aurora OAuth configuration
Token Refresh Errors
Refresh Token Expired:
{
"error" : "invalid_grant" ,
"error_description" : "Token has been expired or revoked"
}
Action: Trigger re-authentication
Network Error:
try :
response = oauth_client.refresh_token(refresh_token)
except requests.exceptions.RequestException as e:
logger.error( f 'Token refresh failed: { e } ' )
# Retry with exponential backoff
Best Practices
Frontend Implementation
class OAuthConnector {
async connect ( provider , userId ) {
// 1. Initiate flow
const { login_url } = await this . getLoginUrl ( provider , userId );
// 2. Open OAuth window
const authWindow = window . open ( login_url , '_blank' , 'width=600,height=700' );
// 3. Poll for completion
const result = await this . pollForCompletion ( authWindow );
return result ;
}
async pollForCompletion ( authWindow ) {
return new Promise (( resolve , reject ) => {
const checkInterval = setInterval (() => {
try {
// Check if window closed
if ( authWindow . closed ) {
clearInterval ( checkInterval );
// Check connection status
this . checkStatus (). then ( resolve );
}
} catch ( e ) {
// Cross-origin error means still on OAuth page
}
}, 500 );
// Timeout after 5 minutes
setTimeout (() => {
clearInterval ( checkInterval );
authWindow . close ();
reject ( new Error ( 'OAuth timeout' ));
}, 300000 );
});
}
}
Secure Callback Handling
@app.route ( '/oauth/callback' )
def oauth_callback ():
# 1. Validate state
state = request.args.get( 'state' )
if not state:
return 'Missing state parameter' , 400
user_id = decode_state(state)
if not user_id:
return 'Invalid state' , 400
# 2. Exchange code for token
code = request.args.get( 'code' )
if not code:
return 'Missing authorization code' , 400
try :
tokens = exchange_code_for_tokens(code)
except OAuthError as e:
logger.error( f 'Token exchange failed: { e } ' )
return redirect( f ' { FRONTEND_URL } ?login=failed' )
# 3. Store tokens securely
store_tokens_in_vault(user_id, tokens)
# 4. Trigger post-auth setup
task = setup_account.delay(user_id)
# 5. Redirect to frontend
return redirect( f ' { FRONTEND_URL } ?login=success&task_id= { task.id } ' )
Token Refresh
def get_access_token ( user_id , provider ):
"""Get valid access token, refreshing if needed."""
tokens = get_tokens_from_vault(user_id, provider)
# Check expiration
if time.time() >= tokens[ 'expires_at' ] - 300 : # 5 min buffer
# Refresh token
new_tokens = refresh_access_token(tokens[ 'refresh_token' ])
store_tokens_in_vault(user_id, new_tokens)
return new_tokens[ 'access_token' ]
return tokens[ 'access_token' ]