Skip to main content
This endpoint allows wallets (operational identities) to authenticate and receive a JWT token that can be used for subsequent API requests.
The username must be provided in the format: walletUsername@appId to uniquely identify which wallet from which App is trying to authenticate.

Endpoint

curl -X POST "https://api.g2cplatform.com/v2/auth/wallet/login" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "sensor01@app_1a2b3c4d5e6f",
    "password": "myStrongPass"
  }'

Request

HTTP Method

POST

URL

https://api.g2cplatform.com/v2/auth/wallet/login

Headers

Content-Type: application/json

Request Body

FieldTypeRequiredDescription
usernamestringUsername in format walletUsername@appId
passwordstringWallet password

Response

Success Response (200 OK)

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3YWxfN2Y4YTliMmMtNGQ1ZS02ZjdnLThoOWktMGoxazJsM200bjVvIiwidXNlcm5hbWUiOiJzZW5zb3IwMSIsImFwcElkIjoiYXBwXzFhMmIzYzRkLTVlNmYtN2c4aC05aTBqLTFrMmwzbTRuNW82cCIsInJvbGUiOiJkZXZpY2UiLCJpYXQiOjE3MjI5MzU4MDAsImV4cCI6MTcyMjkzOTQwMH0.7f8a9b2c4d5e6f7g8h9i0j1k2l3m4n5o",
  "expiresIn": 3600,
  "tokenType": "Bearer"
}

JWT Token Structure

The returned JWT token contains the following claims:
{
  "sub": "wal_7f8a9b2c-4d5e-6f7g-8h9i-0j1k2l3m4n5o", // Wallet ID
  "username": "sensor01",                             // Wallet username
  "appId": "app_1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p", // App ID
  "role": "device",                                   // Role from metadata
  "iat": 1722935800,                                  // Issued at timestamp
  "exp": 1722939400                                   // Expiration timestamp
}

Using the JWT Token

To use the JWT token for authenticated requests, include it in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Error Responses

400 Bad Request

{
  "error": "Invalid request format",
  "code": "INVALID_REQUEST",
  "timestamp": "2025-08-04T10:30:00Z"
}

401 Unauthorized (Invalid Credentials)

{
  "error": "Invalid username or password",
  "code": "INVALID_CREDENTIALS",
  "timestamp": "2025-08-04T10:30:00Z"
}

403 Forbidden (Account Inactive)

{
  "error": "Wallet is suspended or inactive",
  "code": "ACCOUNT_INACTIVE",
  "timestamp": "2025-08-04T10:30:00Z"
}

Implementation Examples

Authentication Service

class AuthService {
  constructor(baseURL = 'https://api.g2cplatform.com/v2') {
    this.baseURL = baseURL;
    this.token = localStorage.getItem('auth_token');
    this.tokenExpiry = localStorage.getItem('auth_token_expiry');
  }

  async login(username, password) {
    try {
      const response = await fetch(`${this.baseURL}/auth/wallet/login`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ username, password })
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.error || 'Login failed');
      }

      const { accessToken, expiresIn } = await response.json();

      // Store the token and its expiration
      this.token = accessToken;
      const expiryTime = Date.now() + (expiresIn * 1000);
      this.tokenExpiry = expiryTime;

      // Save to localStorage for persistence
      localStorage.setItem('auth_token', accessToken);
      localStorage.setItem('auth_token_expiry', expiryTime);

      return { accessToken, expiresIn };
    } catch (error) {
      console.error('Authentication error:', error.message);
      throw error;
    }
  }

  isAuthenticated() {
    if (!this.token || !this.tokenExpiry) {
      return false;
    }

    // Check if token is still valid (not expired)
    return Date.now() < this.tokenExpiry;
  }

  getAuthHeader() {
    if (!this.isAuthenticated()) {
      throw new Error('Not authenticated');
    }

    return {
      'Authorization': `Bearer ${this.token}`
    };
  }

  logout() {
    this.token = null;
    this.tokenExpiry = null;
    localStorage.removeItem('auth_token');
    localStorage.removeItem('auth_token_expiry');
  }
}

// Usage
const authService = new AuthService();

// Login
try {
  await authService.login('sensor01@app_1a2b3c4d5e6f', 'myStrongPass');
  console.log('Login successful');

  // Use the authentication for API requests
  const headers = {
    ...authService.getAuthHeader(),
    'Content-Type': 'application/json'
  };

  const response = await fetch('https://api.g2cplatform.com/v2/objects', {
    headers
  });

  const objects = await response.json();
} catch (error) {
  console.error('Error:', error.message);
}

Automatic Token Refresh

class AuthServiceWithRefresh extends AuthService {
  constructor(baseURL) {
    super(baseURL);
    this.refreshThreshold = 5 * 60 * 1000; // 5 minutes in milliseconds
  }

  async getValidToken() {
    if (!this.token) {
      throw new Error('No authentication token available');
    }

    // Check if token will expire soon
    const timeUntilExpiry = this.tokenExpiry - Date.now();
    if (timeUntilExpiry < this.refreshThreshold) {
      // Token is about to expire, refresh it
      try {
        await this.refreshToken();
      } catch (error) {
        console.error('Failed to refresh token:', error);
        throw new Error('Session expired, please login again');
      }
    }

    return this.token;
  }

  async refreshToken() {
    const response = await fetch(`${this.baseURL}/auth/wallet/refresh`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.token}`
      }
    });

    if (!response.ok) {
      // If refresh fails, clear tokens and require re-login
      this.logout();
      throw new Error('Token refresh failed');
    }

    const { accessToken, expiresIn } = await response.json();

    // Update token information
    this.token = accessToken;
    const expiryTime = Date.now() + (expiresIn * 1000);
    this.tokenExpiry = expiryTime;

    // Update localStorage
    localStorage.setItem('auth_token', accessToken);
    localStorage.setItem('auth_token_expiry', expiryTime);

    return { accessToken, expiresIn };
  }

  async fetchWithAuth(url, options = {}) {
    try {
      // Ensure we have a valid token
      const token = await this.getValidToken();

      // Add authorization header
      const headers = {
        ...options.headers,
        'Authorization': `Bearer ${token}`
      };

      return fetch(url, {
        ...options,
        headers
      });
    } catch (error) {
      console.error('Error making authenticated request:', error);
      throw error;
    }
  }
}

Best Practices

  • Store tokens securely, not in plain text or client-side storage for sensitive applications
  • Never expose tokens in URLs or logs
  • Implement proper token expiration handling
  • Include only necessary claims in the JWT token
  • Implement graceful handling for invalid credentials
  • Add automatic retry logic for network failures
  • Provide clear error messages to users
  • Log authentication failures for security monitoring
  • Implement token refresh before expiration
  • Handle failed refresh attempts gracefully
  • Consider using a refresh token pattern for long-lived sessions
  • Include proper error states in your application for expired tokens
  • Use HTTPS for all API requests
  • Implement rate limiting for login attempts
  • Consider adding MFA for sensitive wallets
  • Rotate credentials periodically for high-security applications

Next Steps