Skip to content

Commit ca3c95c

Browse files
authored
Merge pull request #8 from StackExchange/PKCE-helper
PKCE Authentication feature
2 parents 37545d0 + 9b49452 commit ca3c95c

File tree

11 files changed

+860
-101
lines changed

11 files changed

+860
-101
lines changed

.changeset/tricky-oranges-relax.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"soteams-sdk-docs": minor
3+
"so-teams-sdk": minor
4+
---
5+
6+
Added PKCE authentication helpers for both Backend and Frontend services
Lines changed: 202 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,265 @@
11
---
22
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.
44
---
55
import { Card, CardGrid } from '@astrojs/starlight/components';
66

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.
88

9-
## Overview
9+
### Authentication by Tier
1010

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.
1213

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
1323
```typescript
14-
import StackOverflowSDK from 'so-teams-sdk;
24+
import StackOverflowSDK from 'so-teams-sdk';
1525

26+
// Option 1: OAuth configuration (no token required initially)
1627
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+
}
1935
});
20-
```
2136

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+
```
2350

24-
### For Stack Overflow for Teams Enterprise
51+
#### Basic/Business - Personal Access Token Required
52+
```typescript
53+
import StackOverflowSDK from 'so-teams-sdk';
2554

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+
);
2760

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+
```
2965

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)
3567

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+
:::
3771

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)
3973

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:
4175

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';
4378

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+
});
4585

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();
4788

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
5091

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+
);
5697

98+
// Validate state for CSRF protection
99+
const isValidState = authClient.validateState(callbackState, storedState);
57100
```
58101

59-
## Token Scopes
102+
### Frontend Authentication (Browser)
60103

61-
Different API operations require different scopes. Common scopes include:
104+
The `FrontendAuthClient` communicates with your backend API to handle OAuth securely:
62105

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';
69108

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
71114

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;
73118

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);
79127
```
80128

81-
```typescript title="app.ts"
82-
import StackOverflowSDK from 'so-teams-sdk;
83-
import { config } from './config';
129+
## Authentication Configuration
84130

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+
}
86144
```
87145

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:
89171

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
91180

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>
93195

94-
### Error Handling
196+
## Error Handling
95197

96198
Handle authentication errors gracefully:
97199

98200
```typescript title="auth-error-handling.ts"
99-
async function makeAuthenticatedRequest() {
201+
async function authenticateUser() {
100202
try {
101-
const questions = await sdk.questions.getQuestions();
102-
return questions;
203+
const authUrl = await authClient.getAuthUrl();
204+
// Handle success
103205
} 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);
109210
}
110-
throw error;
111211
}
112212
}
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+
}
113223
```
114224

115-
## Security Best Practices
225+
## Alternative: Personal Access Tokens
116226

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+
```
131250

132251
## Next Steps
133252

134-
Once you have your access token configured:
253+
Once you have authentication configured with the helpers:
135254

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
139258

140259
:::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.
142261
:::
143262

144263
:::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.
146265
:::

docs/src/content/docs/index.mdx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ import { Card, CardGrid } from '@astrojs/starlight/components';
4848
<Card title="Type Safety" icon="laptop">
4949
Complete TypeScript definitions for all Teams endpoints, models, and responses.
5050
</Card>
51+
<Card title="PKCE Authentication Built-in" icon="approve-check-circle">
52+
Includes full PKCE authentication with ready-to-use helpers for both backend and frontend implementations.
53+
</Card>
5154
</CardGrid>
5255

5356
## Popular use cases
@@ -65,7 +68,7 @@ Whether you're building internal tools, integrating with existing workflows, or
6568
## Current status
6669

6770
At the moment, the SDK is only available as a **TypeScript library**.
68-
This project is in **early access**, and more language support and features (such as authentication helpers and rate limiting) will be added in the future.
71+
This project is in **early access**, and more language support and features (such as rate limiting) will be added in the future.
6972

7073
## Ready to start building?
7174

@@ -76,4 +79,4 @@ Our SDK provides a higher-level abstraction for programmatically interacting wit
7679

7780
This Stack Overflow for Teams SDK is licensed to you under the terms of the Apache Software License Version 2.0 (Apache 2.0): https://www.apache.org/licenses/LICENSE-2.0.txt
7881

79-
The Teams SDK is 100% open source and freely available to download and use under the terms of the Apache 2.0 license. This SDK enables development with the Stack Overflow for Teams API, which is available to all Teams customers, but may be subject to future changes.
82+
The Teams SDK is 100% open source and freely available to download and use under the terms of the Apache 2.0 license. This SDK enables development with the Stack Overflow for Teams API, which is available to all Teams customers, but may be subject to future changes.

sdk/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
test.ts
1+
test.ts
2+
*.tgz

0 commit comments

Comments
 (0)