|
1 | 1 | # Auth |
2 | 2 |
|
3 | | -`@dfsync/client` supports auth configuration so you can attach tokens or other credentials to requests. |
| 3 | +`@dfsync/client` supports three auth strategies: |
4 | 4 |
|
5 | | -This allows you to centralize auth logic instead of repeating it in every request. |
| 5 | +- bearer token |
| 6 | +- API key |
| 7 | +- custom auth |
6 | 8 |
|
7 | | -## Example |
| 9 | +Auth is configured once at client creation time. |
| 10 | + |
| 11 | +## Bearer token |
8 | 12 |
|
9 | 13 | ```ts |
10 | | -import { createClient } from '@dfsync/client'; |
| 14 | +const client = createClient({ |
| 15 | + baseUrl: 'https://api.example.com', |
| 16 | + auth: { |
| 17 | + type: 'bearer', |
| 18 | + token: 'secret-token', |
| 19 | + }, |
| 20 | +}); |
| 21 | +``` |
| 22 | + |
| 23 | +This adds: |
11 | 24 |
|
| 25 | +```http |
| 26 | +authorization: Bearer secret-token |
| 27 | +``` |
| 28 | + |
| 29 | +## Async bearer token |
| 30 | + |
| 31 | +You can resolve the token lazily: |
| 32 | + |
| 33 | +```ts |
12 | 34 | const client = createClient({ |
13 | | - baseURL: 'https://api.example.com', |
| 35 | + baseUrl: 'https://api.example.com', |
| 36 | + auth: { |
| 37 | + type: 'bearer', |
| 38 | + token: async () => { |
| 39 | + return process.env.API_TOKEN!; |
| 40 | + }, |
| 41 | + }, |
| 42 | +}); |
| 43 | +``` |
| 44 | + |
| 45 | +## Custom bearer header name |
14 | 46 |
|
15 | | - auth: async ({ request }) => { |
16 | | - request.headers.set('Authorization', 'Bearer TOKEN'); |
| 47 | +```ts |
| 48 | +const client = createClient({ |
| 49 | + baseUrl: 'https://api.example.com', |
| 50 | + auth: { |
| 51 | + type: 'bearer', |
| 52 | + token: 'secret-token', |
| 53 | + headerName: 'x-authorization', |
17 | 54 | }, |
18 | 55 | }); |
19 | 56 | ``` |
20 | 57 |
|
21 | | -Every request sent by the client will include the Authorization header. |
| 58 | +## API key in header |
| 59 | + |
| 60 | +By default, API key auth uses header mode and the header name `x-api-key`. |
| 61 | + |
| 62 | +```ts |
| 63 | +const client = createClient({ |
| 64 | + baseUrl: 'https://api.example.com', |
| 65 | + auth: { |
| 66 | + type: 'apiKey', |
| 67 | + value: 'api-key-123', |
| 68 | + }, |
| 69 | +}); |
| 70 | +``` |
| 71 | + |
| 72 | +This adds: |
| 73 | + |
| 74 | +```http |
| 75 | +x-api-key: api-key-123 |
| 76 | +``` |
| 77 | + |
| 78 | +## Async API key resolver |
| 79 | + |
| 80 | +```ts |
| 81 | +const client = createClient({ |
| 82 | + baseUrl: 'https://api.example.com', |
| 83 | + auth: { |
| 84 | + type: 'apiKey', |
| 85 | + value: async () => { |
| 86 | + return process.env.API_KEY!; |
| 87 | + }, |
| 88 | + }, |
| 89 | +}); |
| 90 | +``` |
| 91 | + |
| 92 | +## Custom API key header name |
| 93 | + |
| 94 | +```ts |
| 95 | +const client = createClient({ |
| 96 | + baseUrl: 'https://api.example.com', |
| 97 | + auth: { |
| 98 | + type: 'apiKey', |
| 99 | + value: 'api-key-123', |
| 100 | + name: 'x-service-key', |
| 101 | + }, |
| 102 | +}); |
| 103 | +``` |
| 104 | + |
| 105 | +## API key in query string |
| 106 | + |
| 107 | +```ts |
| 108 | +const client = createClient({ |
| 109 | + baseUrl: 'https://api.example.com', |
| 110 | + auth: { |
| 111 | + type: 'apiKey', |
| 112 | + value: 'query-key', |
| 113 | + in: 'query', |
| 114 | + name: 'api_key', |
| 115 | + }, |
| 116 | +}); |
| 117 | +``` |
| 118 | + |
| 119 | +A request like: |
| 120 | + |
| 121 | +```ts |
| 122 | +await client.get('/users', { |
| 123 | + query: { page: 1 }, |
| 124 | +}); |
| 125 | +``` |
| 126 | + |
| 127 | +becomes: |
| 128 | + |
| 129 | +```text |
| 130 | +https://api.example.com/users?page=1&api_key=query-key |
| 131 | +``` |
| 132 | + |
| 133 | +## Custom auth |
| 134 | + |
| 135 | +Use custom auth when you need full control over headers and URL mutation. |
| 136 | + |
| 137 | +```ts |
| 138 | +const client = createClient({ |
| 139 | + baseUrl: 'https://api.example.com', |
| 140 | + auth: { |
| 141 | + type: 'custom', |
| 142 | + apply: ({ headers, url, request }) => { |
| 143 | + headers['x-service-name'] = 'billing-worker'; |
| 144 | + url.searchParams.set('tenant', 'acme'); |
| 145 | + }, |
| 146 | + }, |
| 147 | +}); |
| 148 | +``` |
| 149 | + |
| 150 | +## Auth context |
| 151 | + |
| 152 | +Custom auth receives: |
| 153 | + |
| 154 | +```ts |
| 155 | +{ |
| 156 | + (request, url, headers); |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +This lets you inspect the request and modify the final URL or headers before the request is sent. |
| 161 | + |
| 162 | +## Auth execution order |
| 163 | + |
| 164 | +Auth runs before `beforeRequest` hooks. |
| 165 | + |
| 166 | +That means `beforeRequest` hooks can see and further modify headers or query params already produced by auth. |
| 167 | + |
| 168 | +## Auth precedence |
| 169 | + |
| 170 | +If auth writes to a header that already exists, auth wins. |
| 171 | + |
| 172 | +Example: |
| 173 | + |
| 174 | +```ts |
| 175 | +const client = createClient({ |
| 176 | + baseUrl: 'https://api.example.com', |
| 177 | + headers: { |
| 178 | + authorization: 'Bearer old-token', |
| 179 | + }, |
| 180 | + auth: { |
| 181 | + type: 'bearer', |
| 182 | + token: 'new-token', |
| 183 | + }, |
| 184 | +}); |
| 185 | +``` |
| 186 | + |
| 187 | +Final header: |
| 188 | + |
| 189 | +```http |
| 190 | +authorization: Bearer new-token |
| 191 | +``` |
| 192 | + |
| 193 | +## Auth config reference |
| 194 | + |
| 195 | +```ts |
| 196 | +type AuthValueResolver = string | (() => string | Promise<string>); |
| 197 | + |
| 198 | +type BearerAuthConfig = { |
| 199 | + type: 'bearer'; |
| 200 | + token: AuthValueResolver; |
| 201 | + headerName?: string; |
| 202 | +}; |
| 203 | + |
| 204 | +type ApiKeyAuthConfig = { |
| 205 | + type: 'apiKey'; |
| 206 | + value: AuthValueResolver; |
| 207 | + in?: 'header' | 'query'; |
| 208 | + name?: string; |
| 209 | +}; |
| 210 | + |
| 211 | +type CustomAuthConfig = { |
| 212 | + type: 'custom'; |
| 213 | + apply: (ctx: { |
| 214 | + request: RequestConfig; |
| 215 | + url: URL; |
| 216 | + headers: Record<string, string>; |
| 217 | + }) => void | Promise<void>; |
| 218 | +}; |
| 219 | +``` |
22 | 220 |
|
23 | 221 | ## Common use cases |
24 | 222 |
|
|
0 commit comments