|
1 | 1 | ---
|
2 | 2 | title: Authentication
|
3 |
| -description: Learn how to authenticate with the Stack Overflow API to use the SDK. |
| 3 | +description: Learn how to authenticate with the Stack Overflow API using the built-in authentication helpers. |
4 | 4 | ---
|
5 | 5 | import { Card, CardGrid } from '@astrojs/starlight/components';
|
6 | 6 |
|
7 |
| -The Stack Overflow SDK requires an access token to authenticate API requests. This guide explains how to obtain and use access tokens with the SDK. |
| 7 | +The Stack Overflow SDK provides built-in authentication helpers that simplify the OAuth 2.0 flow with PKCE for secure token generation. Authentication requirements vary by Stack Overflow for Teams tier. |
8 | 8 |
|
9 |
| -## Overview |
| 9 | +### Authentication by Tier |
10 | 10 |
|
11 |
| -The SDK uses OAuth 2.0 with access tokens for authentication. All API calls require a valid access token to be passed during SDK initialization. |
| 11 | +- **Stack Overflow for Teams Enterprise** - Supports OAuth 2.0 with PKCE. Access tokens can be generated dynamically. You can optionally provide a single Access Token manually. |
| 12 | +- **Stack Overflow for Teams Basic/Business** - OAuth is not available. Manual input of Access tokens (PATs) are **required** in order to use the SDK. |
12 | 13 |
|
| 14 | +### Authentication Clients |
| 15 | + |
| 16 | +The SDK includes two authentication clients for Enterprise instances: |
| 17 | +- **BackendAuthClient** - For server-side Node.js environments with full PKCE implementation |
| 18 | +- **FrontendAuthClient** - For browser environments that communicate with your backend API |
| 19 | + |
| 20 | +### SDK Initialization Options |
| 21 | + |
| 22 | +#### Enterprise - With OAuth Setup |
13 | 23 | ```typescript
|
14 |
| -import StackOverflowSDK from 'so-teams-sdk; |
| 24 | +import StackOverflowSDK from 'so-teams-sdk'; |
15 | 25 |
|
| 26 | +// Option 1: OAuth configuration (no token required initially) |
16 | 27 | const sdk = new StackOverflowSDK({
|
17 |
| - baseUrl: 'https://[your-site].stackenterprise.co', |
18 |
| - accessToken: 'your-access-token-here' |
| 28 | + baseUrl: 'https://your-site.stackenterprise.co', |
| 29 | + auth: { |
| 30 | + clientId: 'your-client-id', |
| 31 | + redirectUri: 'https://yourapp.com/callback', |
| 32 | + baseUrl: 'https://your-site.stackenterprise.co', |
| 33 | + scope: 'read_inbox write_access' |
| 34 | + } |
19 | 35 | });
|
20 |
| -``` |
21 | 36 |
|
22 |
| -## Obtaining Access Tokens |
| 37 | +// Option 2: With existing access token |
| 38 | +const sdk = StackOverflowSDK.fromToken( |
| 39 | + 'your-access-token', |
| 40 | + 'https://your-site.stackenterprise.co' |
| 41 | +); |
| 42 | + |
| 43 | +// Option 3: Factory method for OAuth setup |
| 44 | +const sdk = StackOverflowSDK.enterpriseOAuth({ |
| 45 | + clientId: 'your-client-id', |
| 46 | + redirectUri: 'https://yourapp.com/callback', |
| 47 | + baseUrl: 'https://your-site.stackenterprise.co' |
| 48 | +}); |
| 49 | +``` |
23 | 50 |
|
24 |
| -### For Stack Overflow for Teams Enterprise |
| 51 | +#### Basic/Business - Personal Access Token Required |
| 52 | +```typescript |
| 53 | +import StackOverflowSDK from 'so-teams-sdk'; |
25 | 54 |
|
26 |
| -If you're using Stack Overflow for Teams Enterprise, follow the comprehensive OAuth implementation guide to generate secure API tokens: |
| 55 | +// PAT is mandatory for Basic/Business tiers |
| 56 | +const sdk = StackOverflowSDK.fromToken( |
| 57 | + 'your-personal-access-token', |
| 58 | + 'https://api.stackoverflowteams.com/v3' |
| 59 | +); |
27 | 60 |
|
28 |
| -**[Secure API Token Generation with OAuth and PKCE →](https://stackoverflow.help/en/articles/19820283-secure-api-token-generation-with-oauth-and-pkce)** |
| 61 | +// Team context is required for Basic/Business operations |
| 62 | +const teamContext = sdk.forTeam('your-team-id'); |
| 63 | +const questions = await teamContext.questions.getAll(); |
| 64 | +``` |
29 | 65 |
|
30 |
| -This guide covers: |
31 |
| -- OAuth Authorization Code flow |
32 |
| -- PKCE (Proof Key for Code Exchange) implementation |
33 |
| -- Token generation and management |
34 |
| -- Security best practices |
| 66 | +## Authentication Clients (Enterprise Only) |
35 | 67 |
|
36 |
| -### For Stack Overflow for Teams |
| 68 | +:::note[Enterprise Feature] |
| 69 | +Authentication clients are only available for Stack Overflow for Teams Enterprise instances. Basic/Business tiers must use Personal Access Tokens during SDK initialization. |
| 70 | +::: |
37 | 71 |
|
38 |
| -If you're using Stack Overflow for Teams (Business or Basic), the process is simpler. You can generate a Personal Access Token (PAT) by following this guide: |
| 72 | +### Backend Authentication (Server-Side) |
39 | 73 |
|
40 |
| -**[Personal Access Tokens (PATs) for API Authentication →](https://stackoverflowteams.help/en/articles/10908790-personal-access-tokens-pats-for-api-authentication)** |
| 74 | +The `BackendAuthClient` handles the complete OAuth flow with PKCE on your server: |
41 | 75 |
|
42 |
| -PATs allow you to authenticate securely with the API without needing the full OAuth flow. |
| 76 | +```typescript title="backend-auth.ts" |
| 77 | +import { BackendAuthClient } from 'so-teams-sdk'; |
43 | 78 |
|
44 |
| -## Using Access Tokens |
| 79 | +const authClient = new BackendAuthClient({ |
| 80 | + clientId: 'your-client-id', |
| 81 | + redirectUri: 'https://yourapp.com/callback', |
| 82 | + baseUrl: 'https://your-site.stackenterprise.co', |
| 83 | + scope: 'read_inbox write_access' // Optional |
| 84 | +}); |
45 | 85 |
|
46 |
| -Once you have an access token, initialize the SDK with your credentials: |
| 86 | +// Generate authorization URL |
| 87 | +const { url, codeVerifier, state } = await authClient.getAuthUrl(); |
47 | 88 |
|
48 |
| -```typescript title="sdk-setup.ts" |
49 |
| -import StackOverflowSDK from 'so-teams-sdk; |
| 89 | +// Store codeVerifier and state securely (session, database, etc.) |
| 90 | +// Redirect user to the authorization URL |
50 | 91 |
|
51 |
| -// For Stack Overflow for Teams |
52 |
| -const teamsSDK = new StackOverflowSDK({ |
53 |
| - baseUrl: 'https://your-site.stackenterprise.co', |
54 |
| - accessToken: 'your-access-token' |
55 |
| -}); |
| 92 | +// In your callback handler: |
| 93 | +const tokens = await authClient.exchangeCodeForToken( |
| 94 | + callbackCode, |
| 95 | + storedCodeVerifier |
| 96 | +); |
56 | 97 |
|
| 98 | +// Validate state for CSRF protection |
| 99 | +const isValidState = authClient.validateState(callbackState, storedState); |
57 | 100 | ```
|
58 | 101 |
|
59 |
| -## Token Scopes |
| 102 | +### Frontend Authentication (Browser) |
60 | 103 |
|
61 |
| -Different API operations require different scopes. Common scopes include: |
| 104 | +The `FrontendAuthClient` communicates with your backend API to handle OAuth securely: |
62 | 105 |
|
63 |
| -| Scope | Description | |
64 |
| -|-------|-------------| |
65 |
| -| read_inbox | Access user's inbox | |
66 |
| -| write_access | Perform write operations | |
67 |
| -| private_info | Access private user data | |
68 |
| -| no_expiry | Token never expires (use with caution) | |
| 106 | +```typescript title="frontend-auth.ts" |
| 107 | +import { FrontendAuthClient } from 'so-teams-sdk'; |
69 | 108 |
|
70 |
| -## Environment Variables |
| 109 | +const frontendClient = new FrontendAuthClient({ |
| 110 | + clientId: 'your-client-id', |
| 111 | + redirectUri: 'https://yourapp.com/callback', |
| 112 | + baseUrl: 'https://your-site.stackenterprise.co' |
| 113 | +}, '/api'); // Your backend API base URL |
71 | 114 |
|
72 |
| -For security, store your access tokens in environment variables: |
| 115 | +// Start authentication flow |
| 116 | +const authUrl = await frontendClient.startAuth(); |
| 117 | +window.location.href = authUrl; |
73 | 118 |
|
74 |
| -```typescript title="config.ts" |
75 |
| -export const config = { |
76 |
| - baseUrl: process.env.STACKOVERFLOW_BASE_URL, |
77 |
| - accessToken: process.env.STACKOVERFLOW_ACCESS_TOKEN |
78 |
| -}; |
| 119 | +// Handle OAuth callback |
| 120 | +await frontendClient.handleCallback(); |
| 121 | + |
| 122 | +// Check authentication status |
| 123 | +const isAuthenticated = await frontendClient.getAuthStatus(); |
| 124 | + |
| 125 | +// Manual token submission (for PATs) |
| 126 | +const success = await frontendClient.submitAccessToken(userToken); |
79 | 127 | ```
|
80 | 128 |
|
81 |
| -```typescript title="app.ts" |
82 |
| -import StackOverflowSDK from 'so-teams-sdk; |
83 |
| -import { config } from './config'; |
| 129 | +## Authentication Configuration |
84 | 130 |
|
85 |
| -const sdk = new StackOverflowSDK(config); |
| 131 | +### AuthConfig Interface |
| 132 | + |
| 133 | +```typescript |
| 134 | +interface AuthConfig { |
| 135 | + /** OAuth client ID from Stack Overflow Enterprise */ |
| 136 | + clientId: string; |
| 137 | + /** Redirect URI registered with your application */ |
| 138 | + redirectUri: string; |
| 139 | + /** Stack Overflow Enterprise instance URL */ |
| 140 | + baseUrl: string; |
| 141 | + /** OAuth scopes (optional) */ |
| 142 | + scope?: string; |
| 143 | +} |
86 | 144 | ```
|
87 | 145 |
|
88 |
| -## Token Management |
| 146 | +### Available Methods |
| 147 | + |
| 148 | +#### BackendAuthClient Methods |
| 149 | + |
| 150 | +| Method | Description | Returns | |
| 151 | +|--------|-------------|---------| |
| 152 | +| `generatePKCETokens()` | Generate PKCE tokens for secure OAuth | `Promise<PKCETokens>` | |
| 153 | +| `getAuthUrl()` | Generate authorization URL with PKCE | `Promise<{url, codeVerifier, state}>` | |
| 154 | +| `exchangeCodeForToken()` | Exchange auth code for access token | `Promise<TokenResponse>` | |
| 155 | +| `validateState()` | Validate state parameter for CSRF protection | `boolean` | |
| 156 | + |
| 157 | +#### FrontendAuthClient Methods |
| 158 | + |
| 159 | +| Method | Description | Returns | |
| 160 | +|--------|-------------|---------| |
| 161 | +| `startAuth()` | Start OAuth flow via backend API | `Promise<string>` | |
| 162 | +| `completeAuth()` | Complete OAuth with callback params | `Promise<void>` | |
| 163 | +| `handleCallback()` | Handle OAuth callback in current page | `Promise<void>` | |
| 164 | +| `getAuthStatus()` | Check if user is authenticated | `Promise<boolean>` | |
| 165 | +| `submitAccessToken()` | Submit manual access token | `Promise<boolean>` | |
| 166 | +| `logout()` | Clear authentication state | `Promise<boolean>` | |
| 167 | + |
| 168 | +## Token Scopes |
| 169 | + |
| 170 | +Different API operations require different scopes: |
89 | 171 |
|
90 |
| -### Token Expiration |
| 172 | +| Scope | Description | |
| 173 | +|-------|-------------| |
| 174 | +| read_inbox | Access user's inbox | |
| 175 | +| write_access | Perform write operations | |
| 176 | +| private_info | Access private user data | |
| 177 | +| no_expiry | Token never expires (use with caution) | |
| 178 | + |
| 179 | +## Security Best Practices |
91 | 180 |
|
92 |
| -Unless you use the `no_expiry` scope, access tokens expire after 24 hours. Monitor token expiration and implement refresh logic as needed. |
| 181 | +<CardGrid> |
| 182 | + <Card title="PKCE Implementation" icon="shield"> |
| 183 | + The SDK automatically implements PKCE (Proof Key for Code Exchange) to prevent authorization code interception attacks. |
| 184 | + </Card> |
| 185 | + <Card title="State Validation" icon="lock"> |
| 186 | + Always validate the state parameter in OAuth callbacks to prevent CSRF attacks using the built-in validation methods. |
| 187 | + </Card> |
| 188 | + <Card title="Secure Token Storage" icon="certificate"> |
| 189 | + Follow secure token storage best practices and avoid client-side storage for sensitive authentication data. |
| 190 | + </Card> |
| 191 | + <Card title="Environment Variables" icon="key"> |
| 192 | + Keep sensitive configuration like client IDs and secrets in environment variables, not in your source code. |
| 193 | + </Card> |
| 194 | +</CardGrid> |
93 | 195 |
|
94 |
| -### Error Handling |
| 196 | +## Error Handling |
95 | 197 |
|
96 | 198 | Handle authentication errors gracefully:
|
97 | 199 |
|
98 | 200 | ```typescript title="auth-error-handling.ts"
|
99 |
| -async function makeAuthenticatedRequest() { |
| 201 | +async function authenticateUser() { |
100 | 202 | try {
|
101 |
| - const questions = await sdk.questions.getQuestions(); |
102 |
| - return questions; |
| 203 | + const authUrl = await authClient.getAuthUrl(); |
| 204 | + // Handle success |
103 | 205 | } catch (error) {
|
104 |
| - if (error.status === 401) { |
105 |
| - console.error('Authentication failed - token may be expired or invalid'); |
106 |
| - // Implement token refresh or re-authentication logic |
107 |
| - } else if (error.status === 403) { |
108 |
| - console.error('Insufficient permissions - check token scopes'); |
| 206 | + if (error.message.includes('clientId')) { |
| 207 | + console.error('Missing OAuth configuration'); |
| 208 | + } else { |
| 209 | + console.error('Authentication setup failed:', error); |
109 | 210 | }
|
110 |
| - throw error; |
111 | 211 | }
|
112 | 212 | }
|
| 213 | + |
| 214 | +// In callback handler |
| 215 | +try { |
| 216 | + const tokens = await authClient.exchangeCodeForToken(code, codeVerifier); |
| 217 | +} catch (error) { |
| 218 | + if (error.message.includes('Failed to exchange')) { |
| 219 | + console.error('Token exchange failed - check OAuth configuration'); |
| 220 | + } |
| 221 | + throw error; |
| 222 | +} |
113 | 223 | ```
|
114 | 224 |
|
115 |
| -## Security Best Practices |
| 225 | +## Alternative: Personal Access Tokens |
116 | 226 |
|
117 |
| -<CardGrid> |
118 |
| - <Card title="Secure Storage" icon="lock"> |
119 |
| - Never hardcode access tokens in your source code. Use environment variables or secure configuration management. |
120 |
| - </Card> |
121 |
| - <Card title="Token Rotation" icon="refresh"> |
122 |
| - Regularly rotate your access tokens and implement proper token lifecycle management. |
123 |
| - </Card> |
124 |
| - <Card title="Scope Limitation" icon="shield"> |
125 |
| - Request only the minimum scopes required for your application functionality. |
126 |
| - </Card> |
127 |
| - <Card title="HTTPS Only" icon="certificate"> |
128 |
| - Always use HTTPS when transmitting access tokens to prevent interception. |
129 |
| - </Card> |
130 |
| -</CardGrid> |
| 227 | +### Enterprise Instances |
| 228 | +For simpler Enterprise implementations, you can generate Tokens manually and use them as seen below: |
| 229 | + |
| 230 | +```typescript |
| 231 | +const sdk = StackOverflowSDK.fromToken( |
| 232 | + 'your-enterprise-pat', |
| 233 | + 'https://your-site.stackenterprise.co' |
| 234 | +); |
| 235 | + |
| 236 | +const questions = await sdk.questions.getAll(); |
| 237 | +``` |
| 238 | + |
| 239 | +You'll need to follow [this guide](https://stackoverflowteams.help/en/articles/9718149-secure-api-token-generation-with-oauth-and-pkce) to understand how OAuth works on Enterprise. |
| 240 | + |
| 241 | +### Basic/Business Instances (Required) |
| 242 | +For Basic and Business tiers, PATs are the only authentication method available: |
| 243 | + |
| 244 | +**[Personal Access Tokens (PATs) for API Authentication →](https://stackoverflowteams.help/en/articles/10908790-personal-access-tokens-pats-for-api-authentication)** |
| 245 | + |
| 246 | +```typescript |
| 247 | +const sdk = StackOverflowSDK.fromToken(userProvidedPAT, 'https://api.stackoverflowteams.com/v3'); |
| 248 | +const teamContext = sdk.forTeam('team-123'); |
| 249 | +``` |
131 | 250 |
|
132 | 251 | ## Next Steps
|
133 | 252 |
|
134 |
| -Once you have your access token configured: |
| 253 | +Once you have authentication configured with the helpers: |
135 | 254 |
|
136 |
| -- [Quickstart Guide](/quickstart) - Get started with the SDK |
137 |
| -- [Questions API](/questions/getall/) - Learn about fetching questions |
138 |
| -- [Rate Limits](/guides/rate-limiting) - Understand how rate limits work on SO Teams |
| 255 | +- [Quickstart Guide](/guides/quickstart) - Get started with the SDK |
| 256 | +- [Questions API](/questions/getall/) - Learn about fetching questions |
| 257 | +- [Rate Limits](/guides/rate-limiting) - Understand API rate limits |
139 | 258 |
|
140 | 259 | :::note[Rate Limits]
|
141 |
| -Authentication doesn't bypass API rate limits. Authenticated requests typically have higher rate limits than anonymous requests, but limits still apply. |
| 260 | +Authentication doesn't bypass API rate limits. |
142 | 261 | :::
|
143 | 262 |
|
144 | 263 | :::tip[Development vs Production]
|
145 |
| -Use different access tokens for development and production environments. Consider using shorter-lived tokens in production for enhanced security. |
| 264 | +Use different OAuth applications and tokens for development and production environments. The authentication helpers support this through environment-specific configuration. |
146 | 265 | :::
|
0 commit comments