Retrieve the profile information for the currently authenticated user. This endpoint returns user details, metadata, and account status.
This endpoint requires authentication. Include the Bearer token in the Authorization header.
curl -X GET "http://localhost:8080/user" \
-H "Authorization: Bearer your_access_token_here"
Response
Unique user identifier (UUID)
Audience claim (typically your application identifier)
User role (e.g., “authenticated”, “admin”)
ISO timestamp when email was confirmed (null if not confirmed)
User’s phone number in E.164 format
ISO timestamp when phone was confirmed (null if not confirmed)
ISO timestamp when user account was confirmed
ISO timestamp of last successful sign-in
Application-specific metadata (managed by your application)
User-specific metadata (can be updated by user)
Array of identity providers linked to this user
ISO timestamp when user account was created
ISO timestamp when user account was last updated
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"aud": "authenticated",
"role": "authenticated",
"email": "user@example.com",
"email_confirmed_at": "2024-01-15T10:30:00Z",
"phone": "+1234567890",
"phone_confirmed_at": "2024-01-15T10:35:00Z",
"confirmed_at": "2024-01-15T10:30:00Z",
"last_sign_in_at": "2024-01-20T14:22:00Z",
"app_metadata": {
"provider": "email",
"providers": ["email", "phone"]
},
"user_metadata": {
"first_name": "John",
"last_name": "Doe",
"avatar_url": "https://example.com/avatars/user123.jpg",
"preferences": {
"theme": "dark",
"notifications": true
}
},
"identities": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"identity_data": {
"email": "user@example.com",
"sub": "123e4567-e89b-12d3-a456-426614174000"
},
"provider": "email",
"last_sign_in_at": "2024-01-20T14:22:00Z",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-20T14:22:00Z"
}
],
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-20T14:22:00Z"
}
Error Responses
{
"code": 401,
"msg": "Invalid token",
"details": "The access token is invalid or expired"
}
Implementation Examples
React User Profile Component
import { useState, useEffect } from 'react';
function UserProfile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
fetchUserProfile();
}, []);
const fetchUserProfile = async () => {
try {
const token = localStorage.getItem('access_token');
if (!token) {
throw new Error('No access token found');
}
const response = await fetch('/api/auth/user', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
if (response.status === 401) {
// Token expired, redirect to login
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
window.location.href = '/login';
return;
}
throw new Error('Failed to fetch user profile');
}
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<div className="profile-loading">
<div className="spinner"></div>
<p>Loading profile...</p>
</div>
);
}
if (error) {
return (
<div className="profile-error">
<p>Error: {error}</p>
<button onClick={fetchUserProfile}>Retry</button>
</div>
);
}
if (!user) {
return <div>No user data available</div>;
}
return (
<div className="user-profile">
<div className="profile-header">
<div className="avatar">
{user.user_metadata?.avatar_url ? (
<img
src={user.user_metadata.avatar_url}
alt="Profile"
className="avatar-image"
/>
) : (
<div className="avatar-placeholder">
{user.user_metadata?.first_name?.[0] || user.email[0].toUpperCase()}
</div>
)}
</div>
<div className="profile-info">
<h1>
{user.user_metadata?.first_name && user.user_metadata?.last_name
? `${user.user_metadata.first_name} ${user.user_metadata.last_name}`
: user.email
}
</h1>
<p className="email">{user.email}</p>
{user.phone && (
<p className="phone">{user.phone}</p>
)}
</div>
</div>
<div className="profile-details">
<div className="detail-section">
<h3>Account Information</h3>
<div className="detail-grid">
<div className="detail-item">
<label>User ID</label>
<span className="user-id">{user.id}</span>
</div>
<div className="detail-item">
<label>Member Since</label>
<span>{new Date(user.created_at).toLocaleDateString()}</span>
</div>
<div className="detail-item">
<label>Last Sign In</label>
<span>
{user.last_sign_in_at
? new Date(user.last_sign_in_at).toLocaleString()
: 'Never'
}
</span>
</div>
<div className="detail-item">
<label>Account Status</label>
<span className={`status ${user.confirmed_at ? 'confirmed' : 'pending'}`}>
{user.confirmed_at ? 'Verified' : 'Pending Verification'}
</span>
</div>
</div>
</div>
<div className="detail-section">
<h3>Verification Status</h3>
<div className="verification-status">
<div className="verification-item">
<span className="verification-label">Email</span>
<span className={`verification-badge ${user.email_confirmed_at ? 'verified' : 'unverified'}`}>
{user.email_confirmed_at ? '✓ Verified' : '⚠ Unverified'}
</span>
</div>
{user.phone && (
<div className="verification-item">
<span className="verification-label">Phone</span>
<span className={`verification-badge ${user.phone_confirmed_at ? 'verified' : 'unverified'}`}>
{user.phone_confirmed_at ? '✓ Verified' : '⚠ Unverified'}
</span>
</div>
)}
</div>
</div>
{user.identities && user.identities.length > 0 && (
<div className="detail-section">
<h3>Connected Accounts</h3>
<div className="identities-list">
{user.identities.map((identity, index) => (
<div key={index} className="identity-item">
<span className="identity-provider">{identity.provider}</span>
<span className="identity-date">
Connected {new Date(identity.created_at).toLocaleDateString()}
</span>
</div>
))}
</div>
</div>
)}
{user.user_metadata && Object.keys(user.user_metadata).length > 0 && (
<div className="detail-section">
<h3>Profile Data</h3>
<div className="metadata-display">
{Object.entries(user.user_metadata).map(([key, value]) => (
<div key={key} className="metadata-item">
<label>{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}</label>
<span>{typeof value === 'object' ? JSON.stringify(value) : String(value)}</span>
</div>
))}
</div>
</div>
)}
</div>
<div className="profile-actions">
<button
onClick={() => window.location.href = '/profile/edit'}
className="edit-profile-btn"
>
Edit Profile
</button>
<button
onClick={() => window.location.href = '/settings'}
className="settings-btn"
>
Account Settings
</button>
</div>
</div>
);
}
export default UserProfile;
React Hook for User Data
import { useState, useEffect, useCallback } from 'react';
function useUser() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchUser = useCallback(async () => {
try {
setLoading(true);
setError(null);
const token = localStorage.getItem('access_token');
if (!token) {
throw new Error('No access token');
}
const response = await fetch('/api/auth/user', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
if (response.status === 401) {
// Clear tokens and redirect to login
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
window.location.href = '/login';
return;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
setUser(null);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchUser();
}, [fetchUser]);
const refreshUser = useCallback(() => {
fetchUser();
}, [fetchUser]);
return {
user,
loading,
error,
refreshUser,
isAuthenticated: !!user,
isEmailVerified: !!user?.email_confirmed_at,
isPhoneVerified: !!user?.phone_confirmed_at,
};
}
export default useUser;
Node.js Backend Handler
const express = require('express');
const { authenticateToken } = require('../middleware/auth');
const router = express.Router();
router.get('/user', authenticateToken, async (req, res) => {
try {
// The user ID is available from the JWT token via middleware
const userId = req.user.sub;
// Call Strike Auth Service to get user details
const response = await fetch(`${process.env.AUTH_SERVICE_URL}/user`, {
method: 'GET',
headers: {
'Authorization': req.headers.authorization,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json();
return res.status(response.status).json(errorData);
}
const userData = await response.json();
// Log user profile access
console.log(`User profile accessed: ${userData.id} (${userData.email})`);
res.json(userData);
} catch (error) {
console.error('Get user profile error:', error);
res.status(500).json({
code: 500,
msg: 'Internal server error',
details: 'Failed to retrieve user profile'
});
}
});
module.exports = router;
Authentication Middleware
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
code: 401,
msg: 'Access token required',
details: 'Please provide a valid access token'
});
}
try {
// Verify the JWT token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
code: 401,
msg: 'Token expired',
details: 'Please refresh your access token'
});
}
return res.status(403).json({
code: 403,
msg: 'Invalid token',
details: 'The provided token is invalid'
});
}
}
module.exports = { authenticateToken };
App metadata is managed by your application and cannot be modified by users:
{
"app_metadata": {
"provider": "email",
"providers": ["email", "phone", "google"],
"roles": ["user"],
"plan": "premium",
"subscription_id": "sub_1234567890",
"created_by": "admin",
"internal_id": "INT_123456"
}
}
User metadata can be updated by users through the update profile endpoint:
{
"user_metadata": {
"first_name": "John",
"last_name": "Doe",
"avatar_url": "https://example.com/avatars/user123.jpg",
"bio": "Software developer passionate about building great products",
"website": "https://johndoe.dev",
"location": "San Francisco, CA",
"timezone": "America/Los_Angeles",
"preferences": {
"theme": "dark",
"language": "en",
"notifications": {
"email": true,
"push": false,
"sms": true
}
},
"social_links": {
"twitter": "@johndoe",
"linkedin": "johndoe",
"github": "johndoe"
}
}
}
Security Considerations
- Token Validation: Always validate JWT tokens server-side
- Scope Limitations: Users can only access their own profile data
- Sensitive Data: Never expose sensitive information in user metadata
- Rate Limiting: Implement rate limiting for profile access
- Audit Logging: Log profile access for security monitoring
Best Practices
- Cache user data appropriately to reduce API calls
- Handle token expiration gracefully with automatic refresh
- Provide loading states for better user experience
- Implement error boundaries for robust error handling
- Use TypeScript for better type safety
- Validate JWT tokens on every request
- Implement proper error handling and logging
- Use middleware for authentication logic
- Cache user data when appropriate
- Monitor API usage and performance
- Keep user metadata lean and relevant
- Use app metadata for application-specific data
- Implement data validation for user inputs
- Consider GDPR compliance for user data
- Regular cleanup of unused metadata fields
Testing
Unit Tests
describe('GET /user', () => {
test('should return user profile for authenticated user', async () => {
const token = await getValidAccessToken();
const response = await request(app)
.get('/user')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.body.id).toBeDefined();
expect(response.body.email).toBeDefined();
expect(response.body.created_at).toBeDefined();
});
test('should return 401 for missing token', async () => {
await request(app)
.get('/user')
.expect(401);
});
test('should return 401 for invalid token', async () => {
await request(app)
.get('/user')
.set('Authorization', 'Bearer invalid_token')
.expect(401);
});
test('should return 401 for expired token', async () => {
const expiredToken = generateExpiredToken();
await request(app)
.get('/user')
.set('Authorization', `Bearer ${expiredToken}`)
.expect(401);
});
});