- Added endpoints for sending and confirming email verification, enhancing user account security. - Integrated two-factor authentication (2FA) with TOTP support, including enabling, verifying, and disabling 2FA. - Implemented social login functionality for Google and Facebook, allowing users to authenticate using their existing accounts. - Updated dependency injection to include services for email, 2FA, and social login. - Enhanced documentation to reflect new features and usage examples for email verification and 2FA.
9.4 KiB
9.4 KiB
IAM Service Authentication Guide
Complete guide for authentication using GoodGo IAM Service with OAuth2/OpenID Connect.
Overview
The IAM Service provides OAuth2/OIDC authentication using Duende IdentityServer:
- Password Grant - User login with email/password
- Refresh Token - Token renewal without re-authentication
- Client Credentials - Service-to-service authentication
- Email Verification - SMTP-based email confirmation
- Two-Factor Authentication (2FA) - TOTP with QR code and recovery codes
- Social Login - Google and Facebook OAuth integration
Quick Start
1. Register a User
curl -X POST http://localhost:5001/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "Password123!",
"firstName": "John",
"lastName": "Doe"
}'
2. Login (Get Tokens)
curl -X POST http://localhost:5001/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "username=user@example.com" \
-d "password=Password123!" \
-d "scope=openid profile email offline_access"
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "eyJhbGciOiJSUzI1NiIs...",
"scope": "openid profile email offline_access"
}
3. Use Access Token
curl http://localhost:5001/api/v1/users/me \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
OAuth2 Grant Types
Password Grant (User Login)
For trusted first-party applications where users enter credentials directly.
curl -X POST http://localhost:5001/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "username=user@example.com" \
-d "password=Password123!" \
-d "scope=openid profile email offline_access"
| Parameter | Required | Description |
|---|---|---|
grant_type |
Yes | Must be password |
username |
Yes | User's email |
password |
Yes | User's password |
scope |
No | Space-separated scopes |
Refresh Token
Renew access token without re-entering credentials.
curl -X POST http://localhost:5001/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=eyJhbGciOiJSUzI1NiIs..."
| Parameter | Required | Description |
|---|---|---|
grant_type |
Yes | Must be refresh_token |
refresh_token |
Yes | Valid refresh token |
Client Credentials (Service-to-Service)
For backend services authenticating without user context.
curl -X POST http://localhost:5001/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=goodgo-service" \
-d "client_secret=service-secret" \
-d "scope=api"
| Parameter | Required | Description |
|---|---|---|
grant_type |
Yes | Must be client_credentials |
client_id |
Yes | Registered client ID |
client_secret |
Yes | Client secret |
scope |
No | Requested scopes |
Note: Client applications must be registered in the database before use.
Token Lifetimes
| Token Type | Lifetime | Use |
|---|---|---|
| Access Token | 15 minutes | API authorization |
| Refresh Token | 7 days | Token renewal |
Available Scopes
| Scope | Description |
|---|---|
openid |
Required for OIDC |
profile |
User profile (name) |
email |
User email |
roles |
User roles |
offline_access |
Get refresh token |
api |
API access |
API Endpoints
Authentication
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/auth/register |
Register new user |
POST |
/connect/token |
OAuth2 token endpoint |
POST |
/api/v1/auth/change-password |
Change password (auth required) |
POST |
/api/v1/auth/logout |
Revoke tokens (auth required) |
Email Verification
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/auth/send-verification-email |
Send email verification link (auth required) |
POST |
/api/v1/auth/confirm-email |
Confirm email with token |
Two-Factor Authentication (2FA)
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/auth/2fa/enable |
Enable 2FA (get QR code) (auth required) |
POST |
/api/v1/auth/2fa/verify |
Verify TOTP code & activate (auth required) |
POST |
/api/v1/auth/2fa/disable |
Disable 2FA (auth required) |
Social Login
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/auth/external-login/{provider} |
Initiate OAuth flow (Google/Facebook) |
GET |
/api/v1/auth/external-callback |
Handle OAuth callback |
GET |
/api/v1/auth/linked-accounts |
Get linked OAuth providers (auth required) |
User Management
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/users |
List users (paginated) |
GET |
/api/v1/users/me |
Current user info |
GET |
/api/v1/users/{id} |
Get user by ID |
PUT |
/api/v1/users/{id} |
Update user |
DELETE |
/api/v1/users/{id} |
Delete user (soft) |
Integration Examples
JavaScript/TypeScript
// Login
const response = await fetch('http://localhost:5001/connect/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'password',
username: 'user@example.com',
password: 'Password123!',
scope: 'openid profile email offline_access'
})
});
const { access_token, refresh_token } = await response.json();
// Use token
const user = await fetch('http://localhost:5001/api/v1/users/me', {
headers: { 'Authorization': `Bearer ${access_token}` }
}).then(r => r.json());
C# / .NET
// Login
var client = new HttpClient();
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["username"] = "user@example.com",
["password"] = "Password123!",
["scope"] = "openid profile email offline_access"
});
var response = await client.PostAsync("http://localhost:5001/connect/token", content);
var tokens = await response.Content.ReadFromJsonAsync<TokenResponse>();
// Use token
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
var user = await client.GetFromJsonAsync<UserDto>("/api/v1/users/me");
Email Verification
Send Verification Email
curl -X POST http://localhost:5001/api/v1/auth/send-verification-email \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'
Confirm Email
curl -X POST http://localhost:5001/api/v1/auth/confirm-email \
-H "Content-Type: application/json" \
-d '{"userId": "user-guid", "token": "confirmation-token"}'
Two-Factor Authentication (2FA)
Enable 2FA
curl -X POST http://localhost:5001/api/v1/auth/2fa/enable \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response:
{
"success": true,
"data": {
"secretKey": "JBSWY3DPEHPK3PXP",
"qrCodeBase64": "data:image/png;base64,...",
"recoveryCodes": ["code1", "code2", "code3"]
}
}
Verify 2FA Code
curl -X POST http://localhost:5001/api/v1/auth/2fa/verify \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"code": "123456"}'
Disable 2FA
curl -X POST http://localhost:5001/api/v1/auth/2fa/disable \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"code": "123456"}'
Social Login
Initiate OAuth Flow
Redirect user to:
GET http://localhost:5001/api/v1/auth/external-login/Google?returnUrl=http://your-app/callback
GET http://localhost:5001/api/v1/auth/external-login/Facebook?returnUrl=http://your-app/callback
Get Linked Accounts
curl http://localhost:5001/api/v1/auth/linked-accounts \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response:
{
"success": true,
"data": {
"linkedProviders": [
{"provider": "Google", "providerDisplayName": "Google"},
{"provider": "Facebook", "providerDisplayName": "Facebook"}
]
}
}
Error Responses
Common Errors
| Error | HTTP Code | Description |
|---|---|---|
invalid_grant |
400 | Invalid credentials |
invalid_client |
400 | Unknown client_id |
invalid_scope |
400 | Invalid scope requested |
unauthorized |
401 | Invalid/expired token |
Error Response Format
{
"error": "invalid_grant",
"error_description": "The username or password is incorrect."
}
Security Best Practices
- Store tokens securely - Use httpOnly cookies or secure storage
- Refresh tokens proactively - Before access token expires
- Use HTTPS in production - Never send credentials over HTTP
- Logout properly - Call logout endpoint to revoke tokens
- Validate tokens server-side - Don't trust client-side validation
Swagger UI
Access interactive API documentation: