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
Content-Type: application/json
Request Body
| Field | Type | Required | Description |
|---|
username | string | ✅ | Username in format walletUsername@appId |
password | string | ✅ | Wallet 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