diff --git a/docs/assets/blog/jwt-authentication-migration.png b/docs/assets/blog/jwt-authentication-migration.png new file mode 100644 index 0000000..42c213d Binary files /dev/null and b/docs/assets/blog/jwt-authentication-migration.png differ diff --git a/docs/blog/index.md b/docs/blog/index.md index 577d7fd..3d59d61 100644 --- a/docs/blog/index.md +++ b/docs/blog/index.md @@ -5,10 +5,12 @@ Welcome to the SMSGate blog! Here you'll find the latest news, updates, and tech ## Categories - [**API**](/blog/category/api/): Technical documentation and integration guides for RESTful API endpoints +- [**Authentication**](/blog/category/authentication/): Strategies and techniques for secure user authentication and authorization - [**Best Practices**](/blog/category/best-practices/): Recommended approaches and optimization techniques for common scenarios - [**Documentation**](/blog/category/documentation/): Comprehensive reference materials and detailed platform guides - [**Features**](/blog/category/features/): In-depth explorations of platform capabilities and advanced functionality - [**IoT**](/blog/category/iot/): Use cases and implementation strategies for Internet of Things applications +- [**Security**](/blog/category/security/): Tips and best practices for secure communication and data protection - [**Tutorials**](/blog/category/tutorials/): Step-by-step implementation guides for specific workflows --- diff --git a/docs/blog/posts/2025-12-09_jwt-authentication-migration.md b/docs/blog/posts/2025-12-09_jwt-authentication-migration.md new file mode 100644 index 0000000..59f05a7 --- /dev/null +++ b/docs/blog/posts/2025-12-09_jwt-authentication-migration.md @@ -0,0 +1,647 @@ +--- +title: "Securing Your SMS Gateway: Migrating from Basic Auth to JWT" +date: 2025-12-10 +categories: + - Security + - Authentication + - API +description: "Learn how JWT authentication enhances security, enables fine-grained access control, and improves performance over Basic Authentication in the SMS Gateway API. Complete migration guide with code examples." +author: SMSGate Team LLM / Claude Sonnet 4.5 +--- +# πŸ” Securing Your SMS Gateway: Migrating from Basic Auth to JWT + +Picture this: Your SMS gateway credentials get accidentally committed to a public GitHub repository. With Basic Authentication, every single API request transmits those credentials, creating countless opportunities for interception. One leaked password means immediate exposure of your entire SMS infrastructure. This scenario isn't hypotheticalβ€”it happens regularly in production environments, leading to security breaches, and unauthorized access. Modern API security demands a better approach. + +Enter JWT (JSON Web Token) authenticationβ€”a token-based authentication mechanism that eliminates the need to transmit credentials with every request while providing fine-grained access control through scopes. In this comprehensive guide, we'll explore why JWT authentication is replacing Basic Auth as the primary authentication method for the SMSGate API, walk through the technical implementation, and provide complete code examples for a smooth migration. Whether you're maintaining existing integrations or building new ones, understanding this transition is essential for securing your SMS infrastructure. + + + +
+ JWT Authentication Migration +
+ +## 🎯 Why JWT Authentication? + +### The Basic Auth Problem + +Basic Authentication has served the web well for decades, but it suffers from fundamental limitations in modern API architectures: + +1. **Credential Transmission**: Username and password are sent with every single request (base64 encoded, but not encrypted) +2. **All-or-Nothing Access**: No way to limit what actions a credential can perform +3. **No Expiration**: Credentials remain valid indefinitely unless manually changed +4. **Difficult to Revoke**: Revoking access requires password changes across all systems +5. **Security Risk**: Credentials exposed in logs, network traces, or compromised systems grant full access + +### JWT Authentication Benefits + +JWT authentication addresses these concerns with a modern, secure approach: + +| Feature | Basic Auth | JWT Authentication | +| -------------------- | ------------------------------------- | ---------------------------------- | +| **Security** | Medium (credentials in every request) | High (token-based with expiration) | +| **Access Control** | All-or-nothing | Fine-grained via scopes | +| **Token Management** | None | Revocation, TTL, refresh | +| **Audit Trail** | Limited | Comprehensive (scopes, expiry) | +| **Recommended For** | Legacy systems only | All new integrations | + +!!! success "Key Advantages" + - **Enhanced Security**: Tokens expire automatically, limiting exposure window + - **Least Privilege**: Request only the permissions you need via scopes + - **Flexible Revocation**: Invalidate specific tokens without affecting others + - **Future-Proof**: Industry-standard approach with broad tooling support + +## πŸ”‘ Understanding JWT Tokens + +### Token Structure + +A JWT token consists of three base64-encoded parts separated by dots: + +```text +Header.Payload.Signature +``` + +**Example Token:** +```text +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwic2NvcGVzIjpbIm1lc3NhZ2VzOnNlbmQiXSwiZXhwIjoxNzMzNzg1MjAwfQ.signature_here +``` + +**Decoded Payload:** +```json +{ + "sub": "user123", + "scopes": ["messages:send", "messages:read"], + "exp": 1733785200, + "iat": 1733781600 +} +``` + +### JWT Scopes + +Scopes implement the principle of least privilege, allowing you to limit what each token can do: + +| Scope | Permission | Use Case | +| ---------------------------------------------------- | -------------------- | -------------------- | +| `messages:send` | Send SMS messages | Frontend application | +| `messages:list`, `messages:read` | Read message history | Analytics dashboard | +| `webhooks:list`, `webhooks:write`, `webhooks:delete` | Manage webhooks | Configuration panel | + +!!! tip "Scope Best Practice" + Always request the minimum scopes necessary. A token for sending messages doesn't need webhook management permissions. + +## πŸš€ Getting Started with JWT + +### Step 1: Generate Your First Token + +To generate a JWT token, make a POST request to the token endpoint using your existing Basic Auth credentials: + +=== "Python" + ```python + import requests + import json + + # Your Basic Auth credentials + USERNAME = "your_username" + PASSWORD = "your_password" + + # Token endpoint + token_url = "https://api.sms-gate.app/3rdparty/v1/auth/token" + + # Token configuration + token_request = { + "ttl": 3600, # Token validity in seconds (1 hour) + "scopes": ["messages:send", "messages:read"] + } + + response = requests.post( + token_url, + auth=(USERNAME, PASSWORD), + headers={"Content-Type": "application/json"}, + json=token_request + ) + + if response.status_code == 201: + token_data = response.json() + access_token = token_data["access_token"] + expires_at = token_data["expires_at"] + + print(f"βœ“ Token generated successfully") + print(f"Token: {access_token[:50]}...") + print(f"Expires: {expires_at}") + else: + print(f"βœ— Error: {response.status_code}") + print(response.text) + ``` + +=== "JavaScript" + ```javascript + const axios = require('axios'); + + // Your Basic Auth credentials + const USERNAME = 'your_username'; + const PASSWORD = 'your_password'; + + // Token endpoint + const tokenUrl = 'https://api.sms-gate.app/3rdparty/v1/auth/token'; + + // Token configuration + const tokenRequest = { + ttl: 3600, // Token validity in seconds (1 hour) + scopes: ['messages:send', 'messages:read'] + }; + + axios.post(tokenUrl, tokenRequest, { + auth: { username: USERNAME, password: PASSWORD }, + headers: { 'Content-Type': 'application/json' } + }) + .then(response => { + const { access_token, expires_at } = response.data; + console.log('βœ“ Token generated successfully'); + console.log(`Token: ${access_token.substring(0, 50)}...`); + console.log(`Expires: ${expires_at}`); + }) + .catch(error => { + console.error(`βœ— Error: ${error.response?.status}`); + console.error(error.response?.data); + }); + ``` + +=== "cURL" + ```bash + curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \ + -u "username:password" \ + -H "Content-Type: application/json" \ + -d '{ + "ttl": 3600, + "scopes": ["messages:send", "messages:read"] + }' + ``` + +**Response:** +```json +{ + "id": "nHDAWaPS6zv3itRUpM9ko", + "token_type": "Bearer", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expires_at": "2025-12-10T03:03:09Z" +} +``` + +### Step 2: Use the JWT Token + +Once you have a token, include it in the `Authorization` header of your API requests: + +=== "Python" + ```python + import requests + + # Your JWT token + access_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + + # Send SMS with JWT + send_url = "https://api.sms-gate.app/3rdparty/v1/messages" + headers = { + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json" + } + + message_data = { + "phoneNumbers": ["+1234567890"], + "textMessage": {"text": "Hello from JWT!"} + } + + response = requests.post(send_url, headers=headers, json=message_data) + + if response.status_code == 200: + print("βœ“ Message sent successfully") + print(response.json()) + else: + print(f"βœ— Error: {response.status_code}") + ``` + +=== "JavaScript" + ```javascript + const axios = require('axios'); + + // Your JWT token + const accessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; + + // Send SMS with JWT + const sendUrl = 'https://api.sms-gate.app/3rdparty/v1/messages'; + const headers = { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json' + }; + + const messageData = { + phoneNumbers: ['+1234567890'], + textMessage: { text: 'Hello from JWT!' } + }; + + axios.post(sendUrl, messageData, { headers }) + .then(response => { + console.log('βœ“ Message sent successfully'); + console.log(response.data); + }) + .catch(error => { + console.error(`βœ— Error: ${error.response?.status}`); + }); + ``` + +=== "cURL" + ```bash + curl -X POST "https://api.sms-gate.app/3rdparty/v1/messages" \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ + -H "Content-Type: application/json" \ + -d '{ + "phoneNumbers": ["+1234567890"], + "textMessage": {"text": "Hello from JWT!"} + }' + ``` + +## πŸ”„ Migration Strategy + +### Phase 1: Dual Authentication Support + +Both Basic Auth and JWT will be supported during the transition period, allowing for gradual migration: + +```python +class SMSGatewayClient: + """Client supporting both Basic Auth and JWT""" + + def __init__(self, base_url, username, password): + self.base_url = base_url + self.username = username + self.password = password + self.access_token = None + self.token_expires_at = None + + def _get_headers(self, use_jwt=True): + """Get appropriate headers based on auth method""" + headers = {"Content-Type": "application/json"} + + if use_jwt and self.access_token: + headers["Authorization"] = f"Bearer {self.access_token}" + + return headers + + def _get_auth(self, use_jwt=True): + """Get auth tuple for Basic Auth""" + if use_jwt: + return None # JWT uses header + return (self.username, self.password) + + def send_message(self, phone_numbers, text, use_jwt=True): + """Send SMS using JWT (default) or Basic Auth""" + response = requests.post( + f"{self.base_url}/messages", + headers=self._get_headers(use_jwt), + auth=self._get_auth(use_jwt), + json={ + "phoneNumbers": phone_numbers, + "textMessage": {"text": text} + } + ) + return response +``` + +### Phase 2: Implement Token Management + +Implement proper token lifecycle management: + +```python +from datetime import datetime, timedelta +import requests + +class JWTTokenManager: + """Manages JWT token lifecycle""" + + def __init__(self, token_url, username, password): + self.token_url = token_url + self.username = username + self.password = password + self.access_token = None + self.expires_at = None + + def get_token(self, scopes, ttl=3600): + """Get a new JWT token""" + response = requests.post( + self.token_url, + auth=(self.username, self.password), + json={"ttl": ttl, "scopes": scopes} + ) + + if response.status_code == 201: + data = response.json() + self.access_token = data["access_token"] + self.expires_at = datetime.fromisoformat( + data["expires_at"].replace("Z", "+00:00") + ) + return self.access_token + else: + raise Exception(f"Token generation failed: {response.text}") + + def is_valid(self): + """Check if current token is still valid""" + if not self.access_token or not self.expires_at: + return False + + # Add 60 second buffer before expiration + return datetime.now(self.expires_at.tzinfo) < ( + self.expires_at - timedelta(seconds=60) + ) + + def ensure_valid_token(self, scopes): + """Ensure we have a valid token, refresh if needed""" + if not self.is_valid(): + return self.get_token(scopes) + return self.access_token + +# Usage +token_manager = JWTTokenManager( + "https://api.sms-gate.app/3rdparty/v1/auth/token", + "username", + "password" +) + +# Always get a valid token +token = token_manager.ensure_valid_token(["messages:send"]) +``` + +### Phase 3: Full JWT Migration + +Complete the migration by removing Basic Auth fallbacks: + +```python +import requests +from datetime import datetime + +class SMSGatewayJWT: + """JWT-only SMS Gateway client""" + + def __init__(self, base_url, username, password): + self.base_url = base_url + self.token_manager = JWTTokenManager( + f"{base_url}/auth/token", + username, + password + ) + + def _make_request(self, method, endpoint, scopes, **kwargs): + """Make authenticated request with automatic token refresh""" + token = self.token_manager.ensure_valid_token(scopes) + + headers = kwargs.pop("headers", {}) + headers["Authorization"] = f"Bearer {token}" + + response = requests.request( + method, + f"{self.base_url}{endpoint}", + headers=headers, + **kwargs + ) + return response + + def send_message(self, phone_numbers, text): + """Send SMS message""" + return self._make_request( + "POST", + "/messages", + scopes=["messages:send"], + json={ + "phoneNumbers": phone_numbers, + "textMessage": {"text": text} + } + ) + + def get_messages(self, limit=50, offset=0): + """Retrieve message history""" + return self._make_request( + "GET", + "/messages", + scopes=["messages:read"], + params={"limit": limit, "offset": offset} + ) +``` + +## πŸ›‘οΈ Security Best Practices + +### 1. Token Storage + +Never store tokens in client-side code or version control: + +```python +import os + +# βœ“ Good: Environment variables +TOKEN_URL = os.getenv("SMS_TOKEN_URL") +USERNAME = os.getenv("SMS_USERNAME") +PASSWORD = os.getenv("SMS_PASSWORD") + +# βœ— Bad: Hardcoded credentials +TOKEN_URL = "https://api.sms-gate.app/3rdparty/v1/auth/token" +USERNAME = "my_username" # Never do this! +PASSWORD = "my_password" # Never do this! +``` + +### 2. Minimal Token TTL + +Use the shortest practical token lifetime: + +```python +# For long-running services +token_manager.get_token(scopes=["messages:send"], ttl=3600) # 1 hour + +# For batch jobs +token_manager.get_token(scopes=["messages:send"], ttl=600) # 10 minutes + +# For one-time operations +token_manager.get_token(scopes=["messages:send"], ttl=300) # 5 minutes +``` + +### 3. Scope Limitation + +Request only necessary scopes: + +```python +# βœ“ Good: Minimal scopes +send_token = get_token(scopes=["messages:send"]) +read_token = get_token(scopes=["messages:read"]) + +# βœ— Bad: Excessive permissions +admin_token = get_token(scopes=["all:any"]) +``` + +### 4. Token Revocation + +Revoke tokens when no longer needed: + +```python +def revoke_token(token, jti): + """Revoke a JWT token""" + response = requests.delete( + f"https://api.sms-gate.app/3rdparty/v1/auth/token/{jti}", + headers={"Authorization": f"Bearer {token}"} + ) + return response.status_code == 204 +``` + +## 🎯 Common Use Cases + +### 1. Frontend Application + +Generate short-lived tokens with limited scopes: + +```javascript +// Token for sending messages only (1 hour) +const frontendToken = await generateToken({ + scopes: ['messages:send'], + ttl: 3600 +}); +``` + +### 2. Analytics Dashboard + +Read-only access to message history: + +```python +# Token for analytics (24 hours) +analytics_token = generate_token( + scopes=["messages:list", "messages:read"], + ttl=86400 +) +``` + +### 3. Admin Tools + +Full access with moderate expiration: + +```python +# Token for administration (4 hours) +admin_token = generate_token( + scopes=["messages:list", "messages:read", "devices:list", "devices:delete", "webhooks:list", "webhooks:write", "webhooks:delete"], + ttl=14400 +) +``` + +### 4. Automated Jobs + +Minimal permissions for batch operations: + +```python +# Token for nightly report generation (1 hour) +batch_token = generate_token( + scopes=["messages:list"], + ttl=3600 +) +``` + +## ⚠️ Troubleshooting + +### Invalid Token Error + +**Problem**: Getting 401 "invalid token" errors + +**Solutions**: +1. Verify token hasn't expired +2. Check authorization header format (should start with "Bearer ") +3. Ensure token was generated successfully +4. Verify server time synchronization + +```python +# Debug token validation +from datetime import datetime +import jwt + +try: + # Decode without verification to inspect + decoded = jwt.decode(token, options={"verify_signature": False}) + exp = datetime.fromtimestamp(decoded['exp']) + + if datetime.now() > exp: + print("βœ— Token expired") + else: + print(f"βœ“ Token valid until {exp}") +except Exception as e: + print(f"βœ— Invalid token format: {e}") +``` + +### Insufficient Permissions + +**Problem**: Getting 403 "forbidden" errors + +**Solution**: Verify token has required scopes + +```python +# Check token scopes +decoded = jwt.decode(token, options={"verify_signature": False}) +scopes = decoded.get('scopes', []) + +required_scope = "messages:send" +if required_scope in scopes: + print(f"βœ“ Token has {required_scope}") +else: + print(f"βœ— Token missing {required_scope}") + print(f"Available scopes: {scopes}") +``` + +## πŸŽ“ Migration Checklist + +Use this checklist for a smooth transition: + +- [ ] **Week 1: Preparation** + - [ ] Review JWT documentation + - [ ] Test token generation in development + - [ ] Identify all services using Basic Auth + - [ ] Plan scope requirements per service + +- [ ] **Week 2: Implementation** + - [ ] Implement token management class + - [ ] Add JWT support to existing clients + - [ ] Create dual-auth fallback mechanism + - [ ] Set up monitoring for auth errors + +- [ ] **Week 3: Testing** + - [ ] Test in staging environment + - [ ] Verify all scopes work correctly + - [ ] Load test JWT performance + - [ ] Document token refresh flows + +- [ ] **Week 4: Deployment** + - [ ] Deploy JWT support to production + - [ ] Monitor error rates + - [ ] Gradually shift traffic to JWT + - [ ] Keep Basic Auth as fallback + +- [ ] **Week 5+: Cleanup** + - [ ] Verify 100% JWT usage + - [ ] Remove Basic Auth code + - [ ] Update all documentation + - [ ] Archive Basic Auth credentials + +## πŸŽ‰ Conclusion + +JWT authentication represents a significant security and performance upgrade over Basic Authentication. By implementing token-based authentication with fine-grained scopes, you gain: + +- **Enhanced security** through time-limited, revocable tokens +- **Fine-grained access control** with scopes +- **Improved auditability** and monitoring +- **Industry-standard** approach with broad tooling support + +The migration process is straightforward with the dual-authentication support during transition. Start by generating your first JWT token today, test it alongside Basic Auth, and gradually migrate your services. The security and performance benefits are well worth the effort. + +Ready to get started? Check out our [Authentication Guide](../../integration/authentication.md) for complete API documentation, or explore our [client libraries](../../integration/client-libraries.md) with built-in JWT support. + +Have questions about JWT migration? Join the discussion on [GitHub](https://github.com/capcom6/android-sms-gateway/discussions) and share your experience with the community! + +## πŸ”— Related Resources + +- [Authentication Guide](../../integration/authentication.md) - Complete JWT documentation +- [Authentication FAQ](../../faq/authentication.md) - Common questions and answers +- [API Reference](../../integration/api.md) - Full API documentation +- [Client Libraries](../../integration/client-libraries.md) - Pre-built JWT integration + +## πŸ“š Related Posts + +- [Mastering Message Retrieval: A Developer's Guide to GET /messages API](./2025-08-07_get-messages-api-guide.md) +- [Targeting Messages to Specific Devices](./2025-07-20_targeting-messages-to-specific-devices.md) +- [Beyond Plain Text: Unlocking the Hidden Power of Data SMS](./2025-07-12_beyond-plain-text-unlocking-data-sms.md) \ No newline at end of file diff --git a/docs/faq/authentication.md b/docs/faq/authentication.md new file mode 100644 index 0000000..745330b --- /dev/null +++ b/docs/faq/authentication.md @@ -0,0 +1,377 @@ +# ❌ FAQ - Authentication + +## πŸ” What is JWT authentication and how does it work? + +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. + +## πŸ”„ How do I migrate from Basic Auth to JWT? + +Migrating from Basic Authentication to JWT provides enhanced security, better performance, and fine-grained access control. Here's how to migrate: + +### Step 1: Update Your Code + +Replace Basic Auth with JWT Bearer tokens: + +=== "Before (Basic Auth)" + ```python + response = requests.post( + "https://api.sms-gate.app/3rdparty/v1/messages", + auth=("username", "password"), + json={"phoneNumbers": ["+1234567890"], "textMessage": {"text": "Hello world!"}} + ) + ``` + +=== "After (JWT)" + ```python + # First, get a token + token_response = requests.post( + "https://api.sms-gate.app/3rdparty/v1/auth/token", + auth=("username", "password"), + json={"ttl": 3600, "scopes": ["messages:send"]} + ) + + if token_response.status_code == 201: + token_data = token_response.json() + access_token = token_data["access_token"] + + # Then use the token + response = requests.post( + "https://api.sms-gate.app/3rdparty/v1/messages", + headers={ + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json" + }, + json={"phoneNumbers": ["+1234567890"], "textMessage": {"text": "Hello world!"}} + ) + ``` + +### Step 2: Implement Token Management + +- **Token Refresh**: Implement automatic token refresh before expiration +- **Error Handling**: Handle 401/403 errors gracefully +- **Secure Storage**: Store tokens securely on the server side + +## πŸ”‘ What are JWT scopes and how do I use them? + +JWT scopes define the permissions associated with a token, implementing the principle of least privilege. All scopes follow the pattern: `resource:action` + +All available scopes are listed in the [Authentication](../integration/authentication.md#jwt-scopes-) section. + +### Using Scopes + +When requesting a JWT token, specify which scopes you need: + +```json +{ + "ttl": 3600, + "scopes": [ + "messages:send", + "messages:read", + "devices:list" + ] +} +``` + +!!! tip "Scope Best Practices" + - Request only the scopes you need + - Create multiple tokens with different scopes for different components + - Use short TTLs for tokens with sensitive scopes + - Avoid using `all:any` unless absolutely necessary + +## ⏰ How long do JWT tokens last and how do I refresh them? + +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. + +### Token Expiration + +```json +{ + "id": "w8pxz0a4Fwa4xgzyCvSeC", + "token_type": "Bearer", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expires_at": "2025-11-22T08:45:00Z" +} +``` + +### Token Refresh Strategy + +Since JWT tokens cannot be refreshed (they must be reissued), implement a proactive refresh strategy: + +```python +import requests +from datetime import datetime, timedelta + +class SMSGatewayClient: + def __init__(self, gateway_url, username, password): + self.gateway_url = gateway_url + self.username = username + self.password = password + self.access_token = None + self.token_expires_at = None + + def get_token(self, scopes, ttl=3600): + """Get a new JWT token""" + response = requests.post( + f"{self.gateway_url}/3rdparty/v1/auth/token", + auth=(self.username, self.password), + headers={"Content-Type": "application/json"}, + json={"ttl": ttl, "scopes": scopes} + ) + + if response.status_code == 201: + token_data = response.json() + self.access_token = token_data["access_token"] + self.token_expires_at = datetime.fromisoformat( + token_data["expires_at"].replace("Z", "+00:00") + ) + return self.access_token + else: + raise Exception(f"Failed to get token: {response.status_code}") + + def ensure_valid_token(self, scopes): + """Ensure we have a valid token, refresh if needed""" + if (self.access_token is None or + self.token_expires_at is None or + datetime.now() + timedelta(minutes=5) >= self.token_expires_at): + return self.get_token(scopes) + return self.access_token +``` + +!!! tip "Token Management Best Practices" + - Refresh tokens 5-10 minutes before expiration + - Implement exponential backoff for failed refresh attempts + - Store tokens securely (not in client-side code) + +## πŸ›‘οΈ How do I revoke a JWT token? + +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. + +### Revoking a Token + +```bash +curl -X DELETE "https://api.sms-gate.app/3rdparty/v1/auth/token/{jti}" \ + -H "Authorization: Basic username:password" +``` + +Where `{jti}` is the token ID from the token response. + +## πŸ” "Invalid token" JWT Error + +The "invalid token" error occurs when the JWT token is malformed, has an incorrect signature, or cannot be validated by the server. + +### Common Causes + +1. **Malformed Token**: The token structure is incorrect or corrupted +2. **Invalid Signature**: The token signature doesn't match the server's secret +3. **Algorithm Mismatch**: The token was signed with a different algorithm than expected +4. **Encoding Issues**: The token contains invalid characters or formatting + +### Troubleshooting Steps + +1. **Check Token Format**: Ensure the token has three parts separated by dots (`.`) + ``` + header.payload.signature + ``` + +2. **Verify Token Copy**: Make sure you copied the entire token without extra spaces or line breaks + +3. **Validate Token Structure**: Use an online JWT decoder to verify the token structure + ```bash + # Check token structure + echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." | tr '.' '\n' + ``` + +## ⏰ "Token expired" JWT Error + +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. + +### Common Causes + +1. **Token TTL Expired**: The token has reached its expiration time +2. **Clock Skew**: Time differences between client and server clocks +3. **Long-running Operations**: Operations that take longer than the token TTL + +### Troubleshooting Steps + +1. **Check Expiration Time**: Parse the token to see when it expires + ```python + import jwt + import datetime + + token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + try: + decoded = jwt.decode(token, options={"verify_signature": False}) + exp_time = datetime.datetime.fromtimestamp(decoded['exp']) + print(f"Token expires at: {exp_time}") + except Exception as e: + print(f"Error decoding token: {e}") + ``` + +2. **Implement Token Refresh**: Refresh tokens before they expire + ```python + # Refresh token 5 minutes before expiration + if datetime.now() + timedelta(minutes=5) >= token_expires_at: + new_token = get_new_token() + ``` + +3. **Adjust Token TTL**: Use a longer TTL for long-running operations + ```json + { + "ttl": 7200, // 2 hours instead of 1 hour + "scopes": ["messages:send", "messages:read"] + } + ``` + +!!! tip "Best Practices" + - Implement automatic token refresh + - Use appropriate TTL values for your use case + - Handle token expiration gracefully in your code + - Consider clock skew in your expiration logic + +## 🚫 "Token revoked" JWT Error + +The "token revoked" error occurs when a JWT token has been manually revoked before its natural expiration time. + +### Common Causes + +1. **Manual Revocation**: Token was explicitly revoked by an administrator +2. **Security Incident**: Token was revoked due to a security concern + +### Troubleshooting Steps + +1. **Request New Token**: Generate a new token with the same scopes + ```bash + curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \ + -u "username:password" \ + -H "Content-Type: application/json" \ + -d '{ + "ttl": 3600, + "scopes": ["messages:send", "messages:read"] + }' + ``` + +2. **Investigate Revocation Reason**: Contact support to understand why the token was revoked + +## πŸ™… "Scope required" JWT Error + +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. + +### Common Causes + +1. **Missing Scope**: The token doesn't include the required scope +2. **Incorrect Scope**: The token has the wrong scope for the requested action +3. **Scope Typos**: The scope name is misspelled or incorrectly formatted +4. **Resource Changes**: The required scope for a resource has changed + +### Troubleshooting Steps + +1. **Check Token Scopes**: Verify what scopes your token contains + ```python + import jwt + + token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + try: + decoded = jwt.decode(token, options={"verify_signature": False}) + print("Token scopes:", decoded.get('scopes', [])) + except Exception as e: + print(f"Error decoding token: {e}") + ``` + +2. **Verify Required Scope**: Check the API documentation for the required scope + ``` + GET /3rdparty/v1/messages requires: messages:list + POST /3rdparty/v1/messages requires: messages:send + ``` + +3. **Request New Token**: Generate a new token with the correct scopes + ```json + { + "ttl": 3600, + "scopes": [ + "messages:send", + "messages:read", + "devices:list" + ] + } + ``` + +!!! tip "Scope Best Practices" + - Request only the scopes you need + - Use the exact scope names from the documentation + - Create multiple tokens for different purposes + - Regularly review and update your scope requirements + +## πŸ”„ "Migration from Basic Auth to JWT" Issues + +When migrating from Basic Authentication to JWT, you may encounter various issues. Here are common problems and their solutions. + +### Common Issues + +1. **Token Generation Errors**: Unable to generate JWT tokens +2. **Permission Errors**: JWT tokens don't have the same permissions as Basic Auth +3. **Code Compatibility**: Existing code doesn't work with JWT authentication + +### Troubleshooting Steps + +1. **Verify Token Generation**: Ensure you can generate JWT tokens successfully + ```bash + curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \ + -u "username:password" \ + -H "Content-Type: application/json" \ + -d '{"ttl": 3600, "scopes": ["messages:send"]}' + ``` + +2. **Update Code Gradually**: Migrate code incrementally rather than all at once + ```python + # Hybrid approach during migration + def make_request(endpoint, data=None, use_jwt=True): + if use_jwt and jwt_token: + headers = {"Authorization": f"Bearer {jwt_token}"} + else: + # Fall back to Basic Auth + headers = {} + auth = (username, password) + + return requests.post(endpoint, headers=headers, auth=auth, json=data) + ``` + +3. **Test in Staging**: Test JWT authentication in a staging environment before production + +!!! tip "Migration Best Practices" + - Keep Basic Auth as a fallback during transition + - Monitor authentication errors during migration + +## πŸ›‘οΈ JWT Security Issues + +JWT tokens are generally secure, but improper implementation can lead to security vulnerabilities. + +### Common Security Issues + +1. **Long TTLs**: Using excessively long token expiration times +2. **Token Leakage**: Tokens being exposed in logs, browser storage, or network traffic +3. **Insufficient Scopes**: Using overly broad scopes like `all:any` + +### Troubleshooting Steps + +1. **Review Token TTL**: Ensure your token TTL is appropriate for your use case + ```json + { + "ttl": 3600, // 1 hour - reasonable for most use cases + "scopes": ["messages:send"] + } + ``` + +2. **Implement Secure Storage**: Ensure tokens are stored securely + ```python + # Example of secure token storage + from cryptography.fernet import Fernet + + key = Fernet.generate_key() + cipher_suite = Fernet(key) + encrypted_token = cipher_suite.encrypt(jwt_token.encode()) + ``` + +!!! tip "Security Best Practices" + - Use the shortest practical TTL for your use case + - Store tokens securely on the server side + - Implement proper token revocation diff --git a/docs/faq/local-server.md b/docs/faq/local-server.md index 1fb27b1..8247717 100644 --- a/docs/faq/local-server.md +++ b/docs/faq/local-server.md @@ -32,4 +32,8 @@ Attempting to connect to the device's API directly can give you an immediate sen ## πŸ”‘ How do I change my password in Local mode? :material-key: -For Local mode, password management is handled through the [Server Configuration](../getting-started/local-server.md#server-configuration) section. \ No newline at end of file +For Local mode, password management is handled through the [Server Configuration](../getting-started/local-server.md#server-configuration) section. + +## πŸ” What authentication methods are supported in Local Server mode? + +Local Server mode **only supports Basic Authentication**. JWT authentication is not available in this mode. For JWT authentication, please use Public Cloud Server or Private Server modes. diff --git a/docs/getting-started/local-server.md b/docs/getting-started/local-server.md index 50f750d..53cc6fe 100644 --- a/docs/getting-started/local-server.md +++ b/docs/getting-started/local-server.md @@ -13,6 +13,9 @@ This mode is ideal for sending messages from a local network, enabling direct co 3. Tap the status button (labeled `Offline`) at the bottom of the screen to start the server; it will switch to `Online` when running. 4. The `Local Server` section will display your device's local and public IP addresses, as well as the credentials for basic authentication. + !!! warning "Authentication Method" + Local Server mode **only supports Basic Authentication**. JWT authentication is not available in this mode. + !!! note "Public IP Accessibility" The displayed public IP address is only accessible from the internet if your device has a public IP assigned by your ISP and your firewall/router allows connections to the specified port (with port forwarding configured). Many ISPs use Carrier-Grade NAT (CG‑NAT), which prevents direct internet access to devices behind shared addresses. See also: [FAQ β€” Local Server](../faq/local-server.md). diff --git a/docs/integration/api.md b/docs/integration/api.md index bf01857..f7f32f4 100644 --- a/docs/integration/api.md +++ b/docs/integration/api.md @@ -1,6 +1,6 @@ # Integration - API πŸ“± -The SMS Gateway for Androidβ„’ provides a robust API that allows you to send SMS messages programmatically from your own applications or services. This enables seamless integration with your existing infrastructure. +The SMSGate provides a robust API that allows you to send SMS messages programmatically from your own applications or services. This enables seamless integration with your existing infrastructure. ## API Specification πŸ“„ @@ -13,3 +13,28 @@ You can find the OpenAPI specification for our API at the following link: [OpenA - **Cloud API**: Accessible from anywhere on the internet External services like Google Apps Script, AWS Lambda, or other cloud functions **cannot** directly access Local Server API endpoints due to network constraints. + +## Authentication πŸ”’ + +The SMSGate API supports two authentication methods: + +1. **Basic Authentication** (Legacy): Simple username/password authentication for backward compatibility +2. **JWT Bearer Tokens** (Recommended): Modern, secure authentication with fine-grained access control + +!!! tip "Recommendation" + For new integrations, we strongly recommend using JWT authentication as it provides better security, scalability, and fine-grained access control through scopes. See [Authentication Guide](authentication.md) for detailed information. + +### Authentication Comparison + +| Feature | JWT Authentication | Basic Authentication | +| ---------------- | ---------------------------------- | ------------------------------------------- | +| Security | High (token-based with expiration) | Medium (credentials sent with each request) | +| Access Control | Fine-grained (scopes) | Coarse-grained (all or nothing) | +| Token Management | Built-in (revocation, TTL) | None | +| Recommended For | All new integrations | Legacy systems only | + +## See Also πŸ”— + +- [Authentication Guide](authentication.md) - Detailed information about JWT authentication +- [Integration Guide](index.md) - Overview of integration options +- [Client Libraries](client-libraries.md) - Pre-built libraries for various languages diff --git a/docs/integration/authentication.md b/docs/integration/authentication.md new file mode 100644 index 0000000..81a61aa --- /dev/null +++ b/docs/integration/authentication.md @@ -0,0 +1,539 @@ +# Integration - Authentication πŸ”’ + +This guide provides a comprehensive overview of authentication in the SMSGate API. JWT authentication is the primary mechanism for securing API access, providing a robust and scalable way to authenticate requests. + +## Authentication Overview πŸ”‘ + +The SMSGate supports multiple authentication methods to accommodate different use cases: + +- **Basic Authentication**: Legacy username/password for backward compatibility +- **JWT Bearer Tokens**: Primary authentication mechanism with configurable TTL + +JWT authentication is recommended for all new integrations as it provides better security, scalability, and fine-grained access control through scopes. + +## JWT Authentication πŸ” + +JWT authentication uses bearer tokens to authenticate API requests. These tokens contain encoded information about the user, their permissions (scopes), and token metadata. + +## Token Generation πŸš€ + +To generate a JWT token, make a POST request to the token endpoint using Basic Authentication. + +### Endpoint + +``` +POST /3rdparty/v1/auth/token +``` + +### Request + +```bash +curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \ + -u "username:password" \ + -H "Content-Type: application/json" \ + -d '{ + "ttl": 3600, + "scopes": [ + "messages:send", + "messages:read", + "devices:list" + ] + }' +``` + +### Request Parameters + +| Parameter | Type | Required | Description | +| --------- | ------- | -------- | --------------------------------------------------------- | +| `ttl` | integer | No | Token time-to-live in seconds (default: server dependent) | +| `scopes` | array | Yes | List of scopes for the token | + +### Response + +```json +{ + "id": "w8pxz0a4Fwa4xgzyCvSeC", + "token_type": "Bearer", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expires_at": "2025-11-22T08:45:00Z" +} +``` + +### Response Fields + +| Field | Type | Description | +| -------------- | ------ | ----------------------------------------- | +| `id` | string | Token identifier (JTI) | +| `token_type` | string | Token type (always "Bearer") | +| `access_token` | string | The JWT token | +| `expires_at` | string | ISO 8601 timestamp when the token expires | + +## Using JWT Tokens πŸ“ + +Once you have a JWT token, include it in the Authorization header of your API requests. + +### Authorization Header Format + +``` +Authorization: Bearer +``` + +### Example Request + +```bash +curl -X GET "https://api.sms-gate.app/3rdparty/v1/messages" \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +``` + +## Token Management πŸ”„ + +### Revoking Tokens + +To revoke a token before it expires, make a DELETE request to the token endpoint. + +``` +DELETE /3rdparty/v1/auth/token/{jti} +``` + +```bash +curl -X DELETE "https://api.sms-gate.app/3rdparty/v1/auth/token/w8pxz0a4Fwa4xgzyCvSeC" \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +``` + +### Token Best Practices + +- **Use short TTLs**: Set appropriate expiration times based on your security requirements +- **Request minimal scopes**: Only request the scopes your application needs +- **Store tokens securely**: Keep tokens in secure storage, not in client-side code +- **Implement token rotation**: Refresh tokens before they expire for long-running applications +- **Revoke unused tokens**: Immediately revoke tokens that are no longer needed + +## JWT Scopes πŸ” + +Scopes define the permissions associated with a JWT token, implementing the principle of least privilege. + +### Scope Structure + +All scopes follow the pattern: `resource:action` + +- **Resource**: The entity being accessed (e.g., `messages`, `devices`) +- **Action**: The operation being performed (e.g., `send`, `read`, `write`) + +### Available Scopes + +#### Global Scope + +| Scope | Description | Access Level | +| --------- | ---------------------------------------------------- | ------------ | +| `all:any` | Provides full access to all resources and operations | Full Access | + +#### Messages Scopes + +| Scope | Description | Access Level | +| --------------- | --------------------------------------------- | ------------ | +| `messages:send` | Permission to send SMS messages | Write | +| `messages:read` | Permission to read individual message details | Read | +| `messages:list` | Permission to list and view messages | Read | + +#### Devices Scopes + +| Scope | Description | Access Level | +| ---------------- | ---------------------------------------------- | ------------ | +| `devices:list` | Permission to list and view registered devices | Read | +| `devices:delete` | Permission to remove/unregister devices | Delete | + +#### Webhooks Scopes + +| Scope | Description | Access Level | +| ----------------- | ------------------------------------------------------ | ------------ | +| `webhooks:list` | Permission to list and view webhook configurations | Read | +| `webhooks:write` | Permission to create and modify webhook configurations | Write | +| `webhooks:delete` | Permission to remove webhook configurations | Delete | + +#### Settings Scopes + +| Scope | Description | Access Level | +| ---------------- | --------------------------------------------- | ------------ | +| `settings:read` | Permission to read system and user settings | Read | +| `settings:write` | Permission to modify system and user settings | Write | + +#### Logs Scopes + +| Scope | Description | Access Level | +| ----------- | ---------------------------------------------- | ------------ | +| `logs:read` | Permission to read system and application logs | Read | + +#### Token Management Scopes + +| Scope | Description | Access Level | +| --------------- | -------------------------------------------- | -------------- | +| `tokens:manage` | Permission to generate and revoke JWT tokens | Administrative | + +### Scope Assignment by Endpoint + +#### Messages API + +| Endpoint | Method | Required Scope | Description | +| ------------------------------------ | ------ | ----------------- | ------------------- | +| `/3rdparty/v1/messages` | GET | `messages:list` | List messages | +| `/3rdparty/v1/messages` | POST | `messages:send` | Send a new message | +| `/3rdparty/v1/messages/:id` | GET | `messages:read` | Get message details | +| `/3rdparty/v1/messages/inbox/export` | POST | `messages:export` | Export messages | + +#### Devices API + +| Endpoint | Method | Required Scope | Description | +| -------------------------- | ------ | ---------------- | ------------- | +| `/3rdparty/v1/devices` | GET | `devices:list` | List devices | +| `/3rdparty/v1/devices/:id` | DELETE | `devices:delete` | Remove device | + +#### Webhooks API + +| Endpoint | Method | Required Scope | Description | +| --------------------------- | ------ | ----------------- | -------------- | +| `/3rdparty/v1/webhooks` | GET | `webhooks:list` | List webhooks | +| `/3rdparty/v1/webhooks` | POST | `webhooks:write` | Create webhook | +| `/3rdparty/v1/webhooks/:id` | DELETE | `webhooks:delete` | Remove webhook | + +#### Settings API + +| Endpoint | Method | Required Scope | Description | +| ----------------------- | ------ | ---------------- | ---------------- | +| `/3rdparty/v1/settings` | GET | `settings:read` | Get settings | +| `/3rdparty/v1/settings` | PATCH | `settings:write` | Update settings | +| `/3rdparty/v1/settings` | PUT | `settings:write` | Replace settings | + +#### Logs API + +| Endpoint | Method | Required Scope | Description | +| ------------------- | ------ | -------------- | ----------- | +| `/3rdparty/v1/logs` | GET | `logs:read` | Read logs | + +#### Token Management API + +| Endpoint | Method | Required Scope | Description | +| ------------------------------ | ------ | --------------- | ------------------ | +| `/3rdparty/v1/auth/token` | POST | `tokens:manage` | Generate new token | +| `/3rdparty/v1/auth/token/:jti` | DELETE | `tokens:manage` | Revoke token | + +## Code Examples πŸ’» + +=== "cURL" + + ```bash title="Generate JWT Token" + curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \ + -u "username:password" \ + -H "Content-Type: application/json" \ + -d '{ + "ttl": 3600, + "scopes": ["messages:send", "messages:read"] + }' + ``` + + ```bash title="Use JWT Token" + curl -X POST "https://api.sms-gate.app/3rdparty/v1/messages" \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ + -H "Content-Type: application/json" \ + -d '{ + "phoneNumbers": ["+1234567890"], + "textMessage": { + "text": "Hello from JWT!" + } + }' + ``` + +=== "Python" + + ```python title="Generate JWT Token" + import requests + import json + + # Configuration + gateway_url = "https://api.sms-gate.app" + username = "your_username" + password = "your_password" + + # Generate token + response = requests.post( + f"{gateway_url}/3rdparty/v1/auth/token", + auth=(username, password), + headers={"Content-Type": "application/json"}, + data=json.dumps({ + "ttl": 3600, + "scopes": ["messages:send", "messages:read"] + }) + ) + + if response.status_code == 201: + token_data = response.json() + access_token = token_data["access_token"] + print(f"Token generated successfully. Expires at: {token_data['expires_at']}") + else: + print(f"Error generating token: {response.status_code} - {response.text}") + ``` + + ```python title="Send SMS with JWT" + import requests + import json + + # Configuration + gateway_url = "https://api.sms-gate.app" + access_token = "your_jwt_token" + + # Send message + response = requests.post( + f"{gateway_url}/3rdparty/v1/messages", + headers={ + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json" + }, + data=json.dumps({ + "phoneNumbers": ["+1234567890"], + "textMessage": {"text": "Hello from JWT!"} + }) + ) + + if response.status_code == 202: + print("Message sent successfully!") + else: + print(f"Error sending message: {response.status_code} - {response.text}") + ``` + +=== "JavaScript" + + ```javascript title="Generate JWT Token" + // Configuration + const gatewayUrl = "https://api.sms-gate.app"; + const username = "your_username"; + const password = "your_password"; + + // Generate token + fetch(`${gatewayUrl}/3rdparty/v1/auth/token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Basic ' + btoa(`${username}:${password}`) + }, + body: JSON.stringify({ + ttl: 3600, + scopes: ["messages:send", "messages:read"] + }) + }) + .then(response => response.json()) + .then(data => { + if (data.access_token) { + console.log('Token generated successfully. Expires at:', data.expires_at); + } else { + console.error('Error generating token:', data); + } + }) + .catch(error => console.error('Error:', error)); + ``` + + ```javascript title="Send SMS with JWT" + // Configuration + const gatewayUrl = "https://api.sms-gate.app"; + const accessToken = "your_jwt_token"; + + // Send message + fetch(`${gatewayUrl}/3rdparty/v1/messages`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + phoneNumbers: ["+1234567890"], + textMessage: {text: "Hello from JWT!"} + }) + }) + .then(response => response.json()) + .then(data => { + if (data.id) { + console.log('Message sent successfully!'); + } else { + console.error('Error sending message:', data); + } + }) + .catch(error => console.error('Error:', error)); + ``` + +## Migration from Basic Auth to JWT πŸ”„ + +### Why Migrate? + +- **Enhanced Security**: JWT tokens provide better security than Basic Auth +- **Fine-grained Access Control**: Scopes allow precise permission management + +### Migration Steps + +1. **Generate JWT Tokens**: Use the token endpoint to create JWT tokens with appropriate scopes +2. **Update Client Code**: Replace Basic Auth with JWT Bearer tokens +3. **Implement Token Management**: Add token refresh and error handling +4. **Test Thoroughly**: Ensure all functionality works with JWT authentication + +### Migration Example + +=== "Before (Basic Auth)" + + ```python + # Basic Auth example + response = requests.post( + "https://api.sms-gate.app/3rdparty/v1/messages", + auth=("username", "password"), + json={ + "phoneNumbers": ["+1234567890"], + "textMessage": {"text": "Hello world!"} + } + ) + ``` + +=== "After (JWT)" + + ```python + # JWT authentication example + # First, get a token + token_response = requests.post( + "https://api.sms-gate.app/3rdparty/v1/auth/token", + auth=("username", "password"), + json={ + "ttl": 3600, + "scopes": ["messages:send"] + } + ) + + if token_response.status_code == 201: + token_data = token_response.json() + access_token = token_data["access_token"] + + # Then use the token + response = requests.post( + "https://api.sms-gate.app/3rdparty/v1/messages", + headers={ + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json" + }, + json={ + "phoneNumbers": ["+1234567890"], + "textMessage": {"text": "Hello world!"} + } + ) + ``` + +## Error Handling ⚠️ + +### Error Handling Best Practices + +1. **Check Token Expiration**: Implement token refresh before expiration +2. **Handle 401 Errors**: Re-authenticate and get a new token +3. **Handle 403 Errors**: Verify your token has the required scopes +4. **Log Errors**: Record authentication errors for debugging + +### Error Handling Example (Python) + +```python +import requests +import time +from datetime import datetime, timedelta + +class SMSGatewayClient: + def __init__(self, gateway_url, username, password): + self.gateway_url = gateway_url + self.username = username + self.password = password + self.access_token = None + self.token_expires_at = None + + def get_token(self, scopes, ttl=3600): + """Get a new JWT token""" + response = requests.post( + f"{self.gateway_url}/3rdparty/v1/auth/token", + auth=(self.username, self.password), + headers={"Content-Type": "application/json"}, + json={"ttl": ttl, "scopes": scopes} + ) + + if response.status_code == 201: + token_data = response.json() + self.access_token = token_data["access_token"] + # Parse expiration time + self.token_expires_at = datetime.fromisoformat( + token_data["expires_at"].replace("Z", "+00:00") + ) + return self.access_token + else: + raise Exception(f"Failed to get token: {response.status_code} - {response.text}") + + def ensure_valid_token(self, scopes): + """Ensure we have a valid token, refresh if needed""" + if (self.access_token is None or + self.token_expires_at is None or + datetime.now() + timedelta(minutes=5) >= self.token_expires_at): + self.get_token(scopes) + return self.access_token + + def send_message(self, recipient, message): + """Send a message with automatic token handling""" + try: + token = self.ensure_valid_token(["messages:send"]) + + response = requests.post( + f"{self.gateway_url}/3rdparty/v1/messages", + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + }, + json={"recipient": recipient, "textMessage": {"text": message}} + ) + + if response.status_code == 401: + # Token might be expired or invalid, try once more + token = self.get_token(["messages:send"]) + response = requests.post( + f"{self.gateway_url}/3rdparty/v1/messages", + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + }, + json={"recipient": recipient, "textMessage": {"text": message}} + ) + + if response.status_code == 202: + return response.json() + else: + raise Exception(f"Failed to send message: {response.status_code} - {response.text}") + + except Exception as e: + print(f"Error sending message: {str(e)}") + raise + +# Usage example +client = SMSGatewayClient("https://api.sms-gate.app", "username", "password") +result = client.send_message("+1234567890", "Hello from JWT!") +print("Message sent:", result) +``` + +## Security Considerations πŸ”’ + +### Token Security + +- **Keep Tokens Secret**: Treat JWT tokens like passwords +- **Use HTTPS**: Always transmit tokens over encrypted connections +- **Short Expiration**: Use the shortest practical TTL for your use case +- **Scope Limitation**: Request only the scopes you need +- **Secure Storage**: Store tokens securely on the server side, not in client-side code +- **Revocation**: Implement token revocation for compromised tokens + +### Client Security + +- **Validate Tokens**: Always validate server responses +- **Error Handling**: Implement proper error handling for authentication failures +- **Token Storage**: Store tokens securely (e.g., HttpOnly cookies, secure server-side storage) +- **CSRF Protection**: Implement CSRF protection for web applications + +## See Also πŸ”— + +- [API Reference](api.md) - Complete API endpoint documentation +- [Integration Guide](index.md) - Overview of integration options +- [Client Libraries](client-libraries.md) - Pre-built libraries for various languages +- [Authentication FAQ](../faq/authentication.md) - Frequently Asked Questions about JWT authentication diff --git a/docs/integration/client-libraries.md b/docs/integration/client-libraries.md index ea63ffe..77c006d 100644 --- a/docs/integration/client-libraries.md +++ b/docs/integration/client-libraries.md @@ -21,3 +21,10 @@ We offer client libraries in various programming languages to assist with integr [:material-github: GitHub Repo](https://github.com/android-sms-gateway/client-py) + +## Support πŸ“ž + +For issues or questions about the client libraries: + +- **GitHub Issues**: Report bugs or request features on the respective repository +- **Documentation**: Refer to the README files in each repository for detailed usage instructions diff --git a/mkdocs.yml b/mkdocs.yml index 86bea70..40467d9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -44,6 +44,7 @@ nav: - Integration: - Overview: integration/index.md - API: integration/api.md + - Authentication: integration/authentication.md - Libraries: integration/client-libraries.md - CLI: integration/cli.md - GET to POST: integration/get-to-post.md @@ -75,6 +76,7 @@ nav: - Webhooks: faq/webhooks.md - Reading Messages: faq/reading-messages.md - Errors: faq/errors.md + - Authentication: faq/authentication.md - Resources: - Examples: resources/examples.md - Third-Party: resources/3rdparty.md