Authentication API

Authentication

Learn how authentication works with Grantiva's API, including tenant identification and JWT token validation.

Overview

Grantiva uses a hybrid authentication model that automatically identifies your tenant based on your Apple Bundle ID and Team ID. No API keys are required - authentication is handled seamlessly through the attestation process.

Authentication Flow

How It Works

  1. Your iOS app initiates attestation with Apple's App Attest service
  2. The attestation includes your app's Bundle ID and Team ID
  3. Grantiva automatically identifies your tenant from these identifiers
  4. Upon successful validation, a JWT token is issued
  5. Use this JWT token for all subsequent API requests

No API Keys Required

Unlike traditional APIs, Grantiva doesn't require managing API keys. Your tenant is automatically identified through the secure App Attest process, eliminating the risk of exposed credentials.

// Traditional approach (NOT needed with Grantiva)
// client.apiKey = "sk_live_abc123..."  ❌

// Grantiva approach - automatic authentication
let grantiva = Grantiva()  // ✅ No configuration needed
let result = try await grantiva.validateAttestation()

JWT Token Structure

After successful attestation, you receive a JWT token with the following structure:

Token Header

{
  "alg": "HS256",
  "typ": "JWT"
}

Token Payload

{
  "sub": "device_key_id_abc123",          // Unique device identifier
  "iss": "grantiva",                      // Token issuer
  "aud": "com.yourcompany.app",           // Your bundle ID
  "iat": 1704063600,                      // Issued at timestamp
  "exp": 1704067200,                      // Expiration timestamp
  "tenantId": "tenant_xyz789",            // Your tenant ID
  "teamId": "ABCD1234",                   // Apple Team ID
  "deviceIntelligence": {
    "deviceId": "device_abc123",
    "riskScore": 15,
    "deviceIntegrity": "high",
    "jailbreakDetected": false,
    "attestationCount": 42,
    "firstSeen": "2024-01-01T00:00:00Z",
    "lastSeen": "2024-01-15T12:00:00Z"
  },
  "permissions": ["basic", "payments", "transfers"],
  "customClaims": {
    // Your custom claims (if configured)
  }
}

Using the JWT Token

Include the JWT token in the Authorization header for all API requests:

HTTP Header Format

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Swift Example

var request = URLRequest(url: apiURL)
request.setValue("Bearer \(jwtToken)", forHTTPHeaderField: "Authorization")

let (data, response) = try await URLSession.shared.data(for: request)

cURL Example

curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
     https://api.grantiva.com/v1/device/status

Token Lifecycle

Token Expiration

  • Default expiration: 1 hour from issuance
  • Configurable per tenant (Professional tier and above)
  • Check the exp claim for exact expiration time

Token Refresh

Tokens cannot be refreshed directly. When a token expires, perform a new attestation:

class TokenManager {
    private var currentToken: String?
    private var tokenExpiration: Date?
    
    func getValidToken() async throws -> String {
        // Check if current token is still valid
        if let token = currentToken, 
           let expiration = tokenExpiration,
           expiration > Date().addingTimeInterval(60) { // 1 minute buffer
            return token
        }
        
        // Perform new attestation
        let grantiva = Grantiva()
        let result = try await grantiva.validateAttestation()
        
        currentToken = result.token
        tokenExpiration = result.expiresAt
        
        return result.token
    }
}

Security Considerations

Token Storage

Store tokens securely in the iOS Keychain, never in UserDefaults or plain text files

Token Transmission

Always use HTTPS when transmitting tokens. Never send tokens via URL parameters

Token Validation

Always validate tokens server-side. Check expiration, signature, and claims

Token Scope

Tokens are scoped to specific devices and cannot be transferred between devices

Error Responses

Authentication errors return standard HTTP status codes:

// 401 Unauthorized - No token provided
{
  "error": "unauthorized",
  "message": "No authentication token provided"
}

// 403 Forbidden - Invalid or expired token
{
  "error": "forbidden",
  "message": "Token expired or invalid"
}

// 403 Forbidden - Device risk too high
{
  "error": "forbidden",
  "message": "Device risk score exceeds threshold",
  "riskScore": 85
}

Next Steps