|
| 1 | +# ❌ FAQ - Authentication |
| 2 | + |
| 3 | +## 🔐 What is JWT authentication and how does it work? |
| 4 | + |
| 5 | +JWT (JSON Web Token) authentication is the primary authentication mechanism for the SMSGate API. It provides a secure, scalable way to authenticate API requests without transmitting credentials with each request. |
| 6 | + |
| 7 | +## 🔄 How do I migrate from Basic Auth to JWT? |
| 8 | + |
| 9 | +Migrating from Basic Authentication to JWT provides enhanced security, better performance, and fine-grained access control. Here's how to migrate: |
| 10 | + |
| 11 | +### Step 1: Update Your Code |
| 12 | + |
| 13 | +Replace Basic Auth with JWT Bearer tokens: |
| 14 | + |
| 15 | +=== "Before (Basic Auth)" |
| 16 | + ```python |
| 17 | + response = requests.post( |
| 18 | + "https://api.sms-gate.app/3rdparty/v1/messages", |
| 19 | + auth=("username", "password"), |
| 20 | + json={"phoneNumbers": ["+1234567890"], "textMessage": {"text": "Hello world!"}} |
| 21 | + ) |
| 22 | + ``` |
| 23 | + |
| 24 | +=== "After (JWT)" |
| 25 | + ```python |
| 26 | + # First, get a token |
| 27 | + token_response = requests.post( |
| 28 | + "https://api.sms-gate.app/3rdparty/v1/auth/token", |
| 29 | + auth=("username", "password"), |
| 30 | + json={"ttl": 3600, "scopes": ["messages:send"]} |
| 31 | + ) |
| 32 | + |
| 33 | + if token_response.status_code == 201: |
| 34 | + token_data = token_response.json() |
| 35 | + access_token = token_data["access_token"] |
| 36 | + |
| 37 | + # Then use the token |
| 38 | + response = requests.post( |
| 39 | + "https://api.sms-gate.app/3rdparty/v1/messages", |
| 40 | + headers={ |
| 41 | + "Authorization": f"Bearer {access_token}", |
| 42 | + "Content-Type": "application/json" |
| 43 | + }, |
| 44 | + json={"phoneNumbers": ["+1234567890"], "textMessage": {"text": "Hello world!"}} |
| 45 | + ) |
| 46 | + ``` |
| 47 | + |
| 48 | +### Step 2: Implement Token Management |
| 49 | + |
| 50 | +- **Token Refresh**: Implement automatic token refresh before expiration |
| 51 | +- **Error Handling**: Handle 401/403 errors gracefully |
| 52 | +- **Secure Storage**: Store tokens securely on the server side |
| 53 | + |
| 54 | +## 🔑 What are JWT scopes and how do I use them? |
| 55 | + |
| 56 | +JWT scopes define the permissions associated with a token, implementing the principle of least privilege. All scopes follow the pattern: `resource:action` |
| 57 | + |
| 58 | +All available scopes are listed in the [Authentication](../integration/authentication.md#jwt-scopes-) section. |
| 59 | + |
| 60 | +### Using Scopes |
| 61 | + |
| 62 | +When requesting a JWT token, specify which scopes you need: |
| 63 | + |
| 64 | +```json |
| 65 | +{ |
| 66 | + "ttl": 3600, |
| 67 | + "scopes": [ |
| 68 | + "messages:send", |
| 69 | + "messages:read", |
| 70 | + "devices:list" |
| 71 | + ] |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +!!! tip "Scope Best Practices" |
| 76 | + - Request only the scopes you need |
| 77 | + - Create multiple tokens with different scopes for different components |
| 78 | + - Use short TTLs for tokens with sensitive scopes |
| 79 | + - Avoid using `all:any` unless absolutely necessary |
| 80 | + |
| 81 | +## ⏰ How long do JWT tokens last and how do I refresh them? |
| 82 | + |
| 83 | +JWT tokens have a configurable time-to-live (TTL). The default TTL is 24 hours (86400 seconds), but you can specify a custom duration when generating a token. |
| 84 | + |
| 85 | +### Token Expiration |
| 86 | + |
| 87 | +```json |
| 88 | +{ |
| 89 | + "id": "w8pxz0a4Fwa4xgzyCvSeC", |
| 90 | + "token_type": "Bearer", |
| 91 | + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", |
| 92 | + "expires_at": "2025-11-22T08:45:00Z" |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +### Token Refresh Strategy |
| 97 | + |
| 98 | +Since JWT tokens cannot be refreshed (they must be reissued), implement a proactive refresh strategy: |
| 99 | + |
| 100 | +```python |
| 101 | +import requests |
| 102 | +from datetime import datetime, timedelta |
| 103 | + |
| 104 | +class SMSGatewayClient: |
| 105 | + def __init__(self, gateway_url, username, password): |
| 106 | + self.gateway_url = gateway_url |
| 107 | + self.username = username |
| 108 | + self.password = password |
| 109 | + self.access_token = None |
| 110 | + self.token_expires_at = None |
| 111 | + |
| 112 | + def get_token(self, scopes, ttl=3600): |
| 113 | + """Get a new JWT token""" |
| 114 | + response = requests.post( |
| 115 | + f"{self.gateway_url}/3rdparty/v1/auth/token", |
| 116 | + auth=(self.username, self.password), |
| 117 | + headers={"Content-Type": "application/json"}, |
| 118 | + json={"ttl": ttl, "scopes": scopes} |
| 119 | + ) |
| 120 | + |
| 121 | + if response.status_code == 201: |
| 122 | + token_data = response.json() |
| 123 | + self.access_token = token_data["access_token"] |
| 124 | + self.token_expires_at = datetime.fromisoformat( |
| 125 | + token_data["expires_at"].replace("Z", "+00:00") |
| 126 | + ) |
| 127 | + return self.access_token |
| 128 | + else: |
| 129 | + raise Exception(f"Failed to get token: {response.status_code}") |
| 130 | + |
| 131 | + def ensure_valid_token(self, scopes): |
| 132 | + """Ensure we have a valid token, refresh if needed""" |
| 133 | + if (self.access_token is None or |
| 134 | + self.token_expires_at is None or |
| 135 | + datetime.now() + timedelta(minutes=5) >= self.token_expires_at): |
| 136 | + return self.get_token(scopes) |
| 137 | + return self.access_token |
| 138 | +``` |
| 139 | + |
| 140 | +!!! tip "Token Management Best Practices" |
| 141 | + - Refresh tokens 5-10 minutes before expiration |
| 142 | + - Implement exponential backoff for failed refresh attempts |
| 143 | + - Store tokens securely (not in client-side code) |
| 144 | + |
| 145 | +## 🛡️ How do I revoke a JWT token? |
| 146 | + |
| 147 | +JWT tokens can be revoked before they expire using the token revocation endpoint. This is useful when a token is compromised or no longer needed. |
| 148 | + |
| 149 | +### Revoking a Token |
| 150 | + |
| 151 | +```bash |
| 152 | +curl -X DELETE "https://api.sms-gate.app/3rdparty/v1/auth/token/{jti}" \ |
| 153 | + -H "Authorization: Basic username:password" |
| 154 | +``` |
| 155 | + |
| 156 | +Where `{jti}` is the token ID from the token response. |
| 157 | + |
| 158 | +## 🔐 "Invalid token" JWT Error |
| 159 | + |
| 160 | +The "invalid token" error occurs when the JWT token is malformed, has an incorrect signature, or cannot be validated by the server. |
| 161 | + |
| 162 | +### Common Causes |
| 163 | + |
| 164 | +1. **Malformed Token**: The token structure is incorrect or corrupted |
| 165 | +2. **Invalid Signature**: The token signature doesn't match the server's secret |
| 166 | +3. **Algorithm Mismatch**: The token was signed with a different algorithm than expected |
| 167 | +4. **Encoding Issues**: The token contains invalid characters or formatting |
| 168 | + |
| 169 | +### Troubleshooting Steps |
| 170 | + |
| 171 | +1. **Check Token Format**: Ensure the token has three parts separated by dots (`.`) |
| 172 | + ``` |
| 173 | + header.payload.signature |
| 174 | + ``` |
| 175 | + |
| 176 | +2. **Verify Token Copy**: Make sure you copied the entire token without extra spaces or line breaks |
| 177 | + |
| 178 | +3. **Validate Token Structure**: Use an online JWT decoder to verify the token structure |
| 179 | + ```bash |
| 180 | + # Check token structure |
| 181 | + echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." | tr '.' '\n' |
| 182 | + ``` |
| 183 | + |
| 184 | +## ⏰ "Token expired" JWT Error |
| 185 | + |
| 186 | +The "token expired" error occurs when the JWT token has passed its expiration time. This is a normal part of the JWT lifecycle and requires token refresh. |
| 187 | + |
| 188 | +### Common Causes |
| 189 | + |
| 190 | +1. **Token TTL Expired**: The token has reached its expiration time |
| 191 | +2. **Clock Skew**: Time differences between client and server clocks |
| 192 | +3. **Long-running Operations**: Operations that take longer than the token TTL |
| 193 | + |
| 194 | +### Troubleshooting Steps |
| 195 | + |
| 196 | +1. **Check Expiration Time**: Parse the token to see when it expires |
| 197 | + ```python |
| 198 | + import jwt |
| 199 | + import datetime |
| 200 | + |
| 201 | + token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." |
| 202 | + try: |
| 203 | + decoded = jwt.decode(token, options={"verify_signature": False}) |
| 204 | + exp_time = datetime.datetime.fromtimestamp(decoded['exp']) |
| 205 | + print(f"Token expires at: {exp_time}") |
| 206 | + except Exception as e: |
| 207 | + print(f"Error decoding token: {e}") |
| 208 | + ``` |
| 209 | + |
| 210 | +2. **Implement Token Refresh**: Refresh tokens before they expire |
| 211 | + ```python |
| 212 | + # Refresh token 5 minutes before expiration |
| 213 | + if datetime.now() + timedelta(minutes=5) >= token_expires_at: |
| 214 | + new_token = get_new_token() |
| 215 | + ``` |
| 216 | + |
| 217 | +3. **Adjust Token TTL**: Use a longer TTL for long-running operations |
| 218 | + ```json |
| 219 | + { |
| 220 | + "ttl": 7200, // 2 hours instead of 1 hour |
| 221 | + "scopes": ["messages:send", "messages:read"] |
| 222 | + } |
| 223 | + ``` |
| 224 | + |
| 225 | +!!! tip "Best Practices" |
| 226 | + - Implement automatic token refresh |
| 227 | + - Use appropriate TTL values for your use case |
| 228 | + - Handle token expiration gracefully in your code |
| 229 | + - Consider clock skew in your expiration logic |
| 230 | + |
| 231 | +## 🚫 "Token revoked" JWT Error |
| 232 | + |
| 233 | +The "token revoked" error occurs when a JWT token has been manually revoked before its natural expiration time. |
| 234 | + |
| 235 | +### Common Causes |
| 236 | + |
| 237 | +1. **Manual Revocation**: Token was explicitly revoked by an administrator |
| 238 | +2. **Security Incident**: Token was revoked due to a security concern |
| 239 | + |
| 240 | +### Troubleshooting Steps |
| 241 | + |
| 242 | +1. **Request New Token**: Generate a new token with the same scopes |
| 243 | + ```bash |
| 244 | + curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \ |
| 245 | + -u "username:password" \ |
| 246 | + -H "Content-Type: application/json" \ |
| 247 | + -d '{ |
| 248 | + "ttl": 3600, |
| 249 | + "scopes": ["messages:send", "messages:read"] |
| 250 | + }' |
| 251 | + ``` |
| 252 | + |
| 253 | +2. **Investigate Revocation Reason**: Contact support to understand why the token was revoked |
| 254 | + |
| 255 | +## 🙅 "Scope required" JWT Error |
| 256 | + |
| 257 | +The "scope required" error occurs when the JWT token doesn't have the necessary scope to access a specific resource or perform a specific action. |
| 258 | + |
| 259 | +### Common Causes |
| 260 | + |
| 261 | +1. **Missing Scope**: The token doesn't include the required scope |
| 262 | +2. **Incorrect Scope**: The token has the wrong scope for the requested action |
| 263 | +3. **Scope Typos**: The scope name is misspelled or incorrectly formatted |
| 264 | +4. **Resource Changes**: The required scope for a resource has changed |
| 265 | + |
| 266 | +### Troubleshooting Steps |
| 267 | + |
| 268 | +1. **Check Token Scopes**: Verify what scopes your token contains |
| 269 | + ```python |
| 270 | + import jwt |
| 271 | + |
| 272 | + token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." |
| 273 | + try: |
| 274 | + decoded = jwt.decode(token, options={"verify_signature": False}) |
| 275 | + print("Token scopes:", decoded.get('scopes', [])) |
| 276 | + except Exception as e: |
| 277 | + print(f"Error decoding token: {e}") |
| 278 | + ``` |
| 279 | + |
| 280 | +2. **Verify Required Scope**: Check the API documentation for the required scope |
| 281 | + ``` |
| 282 | + GET /3rdparty/v1/messages requires: messages:list |
| 283 | + POST /3rdparty/v1/messages requires: messages:send |
| 284 | + ``` |
| 285 | + |
| 286 | +3. **Request New Token**: Generate a new token with the correct scopes |
| 287 | + ```json |
| 288 | + { |
| 289 | + "ttl": 3600, |
| 290 | + "scopes": [ |
| 291 | + "messages:send", |
| 292 | + "messages:read", |
| 293 | + "devices:list" |
| 294 | + ] |
| 295 | + } |
| 296 | + ``` |
| 297 | + |
| 298 | +!!! tip "Scope Best Practices" |
| 299 | + - Request only the scopes you need |
| 300 | + - Use the exact scope names from the documentation |
| 301 | + - Create multiple tokens for different purposes |
| 302 | + - Regularly review and update your scope requirements |
| 303 | + |
| 304 | +## 🔄 "Migration from Basic Auth to JWT" Issues |
| 305 | + |
| 306 | +When migrating from Basic Authentication to JWT, you may encounter various issues. Here are common problems and their solutions. |
| 307 | + |
| 308 | +### Common Issues |
| 309 | + |
| 310 | +1. **Token Generation Errors**: Unable to generate JWT tokens |
| 311 | +2. **Permission Errors**: JWT tokens don't have the same permissions as Basic Auth |
| 312 | +3. **Code Compatibility**: Existing code doesn't work with JWT authentication |
| 313 | + |
| 314 | +### Troubleshooting Steps |
| 315 | + |
| 316 | +1. **Verify Token Generation**: Ensure you can generate JWT tokens successfully |
| 317 | + ```bash |
| 318 | + curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \ |
| 319 | + -u "username:password" \ |
| 320 | + -H "Content-Type: application/json" \ |
| 321 | + -d '{"ttl": 3600, "scopes": ["messages:send"]}' |
| 322 | + ``` |
| 323 | + |
| 324 | +2. **Update Code Gradually**: Migrate code incrementally rather than all at once |
| 325 | + ```python |
| 326 | + # Hybrid approach during migration |
| 327 | + def make_request(endpoint, data=None, use_jwt=True): |
| 328 | + if use_jwt and jwt_token: |
| 329 | + headers = {"Authorization": f"Bearer {jwt_token}"} |
| 330 | + else: |
| 331 | + # Fall back to Basic Auth |
| 332 | + headers = {} |
| 333 | + auth = (username, password) |
| 334 | + |
| 335 | + return requests.post(endpoint, headers=headers, auth=auth, json=data) |
| 336 | + ``` |
| 337 | + |
| 338 | +3. **Test in Staging**: Test JWT authentication in a staging environment before production |
| 339 | + |
| 340 | +!!! tip "Migration Best Practices" |
| 341 | + - Keep Basic Auth as a fallback during transition |
| 342 | + - Monitor authentication errors during migration |
| 343 | + |
| 344 | +## 🛡️ JWT Security Issues |
| 345 | + |
| 346 | +JWT tokens are generally secure, but improper implementation can lead to security vulnerabilities. |
| 347 | + |
| 348 | +### Common Security Issues |
| 349 | + |
| 350 | +1. **Long TTLs**: Using excessively long token expiration times |
| 351 | +2. **Token Leakage**: Tokens being exposed in logs, browser storage, or network traffic |
| 352 | +3. **Insufficient Scopes**: Using overly broad scopes like `all:any` |
| 353 | + |
| 354 | +### Troubleshooting Steps |
| 355 | + |
| 356 | +1. **Review Token TTL**: Ensure your token TTL is appropriate for your use case |
| 357 | + ```json |
| 358 | + { |
| 359 | + "ttl": 3600, // 1 hour - reasonable for most use cases |
| 360 | + "scopes": ["messages:send"] |
| 361 | + } |
| 362 | + ``` |
| 363 | + |
| 364 | +2. **Implement Secure Storage**: Ensure tokens are stored securely |
| 365 | + ```python |
| 366 | + # Example of secure token storage |
| 367 | + from cryptography.fernet import Fernet |
| 368 | + |
| 369 | + key = Fernet.generate_key() |
| 370 | + cipher_suite = Fernet(key) |
| 371 | + encrypted_token = cipher_suite.encrypt(jwt_token.encode()) |
| 372 | + ``` |
| 373 | + |
| 374 | +!!! tip "Security Best Practices" |
| 375 | + - Use the shortest practical TTL for your use case |
| 376 | + - Store tokens securely on the server side |
| 377 | + - Implement proper token revocation |
0 commit comments