@doist/todoist-sdk 10.4.0 → 10.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/cjs/authentication.js +54 -11
- package/dist/esm/authentication.js +53 -11
- package/dist/types/authentication.d.ts +42 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -104,6 +104,27 @@ await revokeToken(args, {
|
|
|
104
104
|
const { accessToken } = await getAuthToken(args, baseUrl)
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
When the authorization server issues a `refreshToken`, use `refreshAuthToken`
|
|
108
|
+
to obtain a new access token once the current one expires, without sending the
|
|
109
|
+
user back through the authorization flow:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const refreshed = await refreshAuthToken({
|
|
113
|
+
clientId: 'your-client-id',
|
|
114
|
+
clientSecret: 'your-client-secret',
|
|
115
|
+
refreshToken: storedRefreshToken,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// The server may rotate the refresh token — persist refreshed.refreshToken
|
|
119
|
+
// when present, otherwise keep using the previous one.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
> **Note:** Refresh tokens are only issued to OAuth apps that have them enabled
|
|
123
|
+
> (the default for new apps). Apps without them receive a long-lived access
|
|
124
|
+
> token, so `getAuthToken` returns no `refreshToken` and there is nothing to
|
|
125
|
+
> refresh. For public clients (`tokenEndpointAuthMethod: 'none'`), omit
|
|
126
|
+
> `clientSecret` when calling `refreshAuthToken`.
|
|
127
|
+
|
|
107
128
|
### Important Notes
|
|
108
129
|
|
|
109
130
|
- All existing transforms (snake_case ↔ camelCase) work automatically with custom fetch
|
|
@@ -15,6 +15,7 @@ exports.TOKEN_ENDPOINT_AUTH_METHODS = exports.PERMISSIONS = void 0;
|
|
|
15
15
|
exports.getAuthStateParameter = getAuthStateParameter;
|
|
16
16
|
exports.getAuthorizationUrl = getAuthorizationUrl;
|
|
17
17
|
exports.getAuthToken = getAuthToken;
|
|
18
|
+
exports.refreshAuthToken = refreshAuthToken;
|
|
18
19
|
exports.revokeToken = revokeToken;
|
|
19
20
|
exports.migratePersonalToken = migratePersonalToken;
|
|
20
21
|
exports.registerClient = registerClient;
|
|
@@ -108,32 +109,74 @@ function getAuthorizationUrl({ clientId, permissions, state, baseUrl, }) {
|
|
|
108
109
|
* @returns The access token response
|
|
109
110
|
* @throws {@link TodoistRequestError} If the token exchange fails
|
|
110
111
|
*/
|
|
111
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Posts a payload to the OAuth token endpoint and returns the token response,
|
|
114
|
+
* mapping any failure to a {@link TodoistRequestError} with `errorMessage`.
|
|
115
|
+
* Shared by {@link getAuthToken} and {@link refreshAuthToken}.
|
|
116
|
+
*/
|
|
117
|
+
async function requestAuthToken(payload, errorMessage, options) {
|
|
112
118
|
var _a;
|
|
113
|
-
if (typeof options === 'string') {
|
|
114
|
-
throw new TypeError('Passing baseUrl as a string is no longer supported. Use an options object instead: getAuthToken(args, { baseUrl })');
|
|
115
|
-
}
|
|
116
|
-
const baseUrl = options === null || options === void 0 ? void 0 : options.baseUrl;
|
|
117
|
-
const customFetch = options === null || options === void 0 ? void 0 : options.customFetch;
|
|
118
119
|
try {
|
|
119
120
|
const response = await (0, http_client_1.request)({
|
|
120
121
|
httpMethod: 'POST',
|
|
121
|
-
baseUri: (0, endpoints_1.getAuthBaseUri)(baseUrl),
|
|
122
|
+
baseUri: (0, endpoints_1.getAuthBaseUri)(options === null || options === void 0 ? void 0 : options.baseUrl),
|
|
122
123
|
relativePath: endpoints_1.ENDPOINT_GET_TOKEN,
|
|
123
124
|
apiToken: undefined,
|
|
124
|
-
payload
|
|
125
|
-
customFetch,
|
|
125
|
+
payload,
|
|
126
|
+
customFetch: options === null || options === void 0 ? void 0 : options.customFetch,
|
|
126
127
|
});
|
|
127
128
|
if (response.status !== 200 || !((_a = response.data) === null || _a === void 0 ? void 0 : _a.accessToken)) {
|
|
128
|
-
throw new types_1.TodoistRequestError(
|
|
129
|
+
throw new types_1.TodoistRequestError(errorMessage, response.status, response.data);
|
|
129
130
|
}
|
|
130
131
|
return response.data;
|
|
131
132
|
}
|
|
132
133
|
catch (error) {
|
|
133
134
|
// Re-throw with custom message for authentication failures
|
|
134
135
|
const err = error;
|
|
135
|
-
throw new types_1.TodoistRequestError(
|
|
136
|
+
throw new types_1.TodoistRequestError(errorMessage, err.httpStatusCode, err.responseData);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function getAuthToken(args, options) {
|
|
140
|
+
if (typeof options === 'string') {
|
|
141
|
+
throw new TypeError('Passing baseUrl as a string is no longer supported. Use an options object instead: getAuthToken(args, { baseUrl })');
|
|
142
|
+
}
|
|
143
|
+
return requestAuthToken(Object.assign({}, args), 'Authentication token exchange failed.', options);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Exchanges a refresh token for a new access token using the OAuth2
|
|
147
|
+
* `refresh_token` grant.
|
|
148
|
+
*
|
|
149
|
+
* Only relevant for OAuth apps that have refresh tokens enabled (the default for
|
|
150
|
+
* new apps). Apps without them never receive a `refreshToken` from
|
|
151
|
+
* {@link getAuthToken}, so they have nothing to pass here — their access tokens
|
|
152
|
+
* are long-lived instead.
|
|
153
|
+
*
|
|
154
|
+
* Omit `clientSecret` for public clients registered with
|
|
155
|
+
* `tokenEndpointAuthMethod: 'none'`.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* const { accessToken, refreshToken } = await refreshAuthToken({
|
|
160
|
+
* clientId: 'your-client-id',
|
|
161
|
+
* clientSecret: 'your-client-secret',
|
|
162
|
+
* refreshToken: storedRefreshToken,
|
|
163
|
+
* })
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* @returns The refreshed token. The server rotates the refresh token, so the
|
|
167
|
+
* response carries a new `refreshToken` — persist it in place of the old one.
|
|
168
|
+
* @throws {@link TodoistRequestError} If the refresh fails
|
|
169
|
+
*/
|
|
170
|
+
async function refreshAuthToken(args, options) {
|
|
171
|
+
const payload = {
|
|
172
|
+
clientId: args.clientId,
|
|
173
|
+
refreshToken: args.refreshToken,
|
|
174
|
+
grantType: 'refresh_token',
|
|
175
|
+
};
|
|
176
|
+
if (args.clientSecret !== undefined) {
|
|
177
|
+
payload.clientSecret = args.clientSecret;
|
|
136
178
|
}
|
|
179
|
+
return requestAuthToken(payload, 'Authentication token refresh failed.', options);
|
|
137
180
|
}
|
|
138
181
|
/**
|
|
139
182
|
* Revokes a token using the RFC 7009 OAuth 2.0 Token Revocation standard.
|
|
@@ -99,32 +99,74 @@ export function getAuthorizationUrl({ clientId, permissions, state, baseUrl, })
|
|
|
99
99
|
* @returns The access token response
|
|
100
100
|
* @throws {@link TodoistRequestError} If the token exchange fails
|
|
101
101
|
*/
|
|
102
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Posts a payload to the OAuth token endpoint and returns the token response,
|
|
104
|
+
* mapping any failure to a {@link TodoistRequestError} with `errorMessage`.
|
|
105
|
+
* Shared by {@link getAuthToken} and {@link refreshAuthToken}.
|
|
106
|
+
*/
|
|
107
|
+
async function requestAuthToken(payload, errorMessage, options) {
|
|
103
108
|
var _a;
|
|
104
|
-
if (typeof options === 'string') {
|
|
105
|
-
throw new TypeError('Passing baseUrl as a string is no longer supported. Use an options object instead: getAuthToken(args, { baseUrl })');
|
|
106
|
-
}
|
|
107
|
-
const baseUrl = options === null || options === void 0 ? void 0 : options.baseUrl;
|
|
108
|
-
const customFetch = options === null || options === void 0 ? void 0 : options.customFetch;
|
|
109
109
|
try {
|
|
110
110
|
const response = await request({
|
|
111
111
|
httpMethod: 'POST',
|
|
112
|
-
baseUri: getAuthBaseUri(baseUrl),
|
|
112
|
+
baseUri: getAuthBaseUri(options === null || options === void 0 ? void 0 : options.baseUrl),
|
|
113
113
|
relativePath: ENDPOINT_GET_TOKEN,
|
|
114
114
|
apiToken: undefined,
|
|
115
|
-
payload
|
|
116
|
-
customFetch,
|
|
115
|
+
payload,
|
|
116
|
+
customFetch: options === null || options === void 0 ? void 0 : options.customFetch,
|
|
117
117
|
});
|
|
118
118
|
if (response.status !== 200 || !((_a = response.data) === null || _a === void 0 ? void 0 : _a.accessToken)) {
|
|
119
|
-
throw new TodoistRequestError(
|
|
119
|
+
throw new TodoistRequestError(errorMessage, response.status, response.data);
|
|
120
120
|
}
|
|
121
121
|
return response.data;
|
|
122
122
|
}
|
|
123
123
|
catch (error) {
|
|
124
124
|
// Re-throw with custom message for authentication failures
|
|
125
125
|
const err = error;
|
|
126
|
-
throw new TodoistRequestError(
|
|
126
|
+
throw new TodoistRequestError(errorMessage, err.httpStatusCode, err.responseData);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export async function getAuthToken(args, options) {
|
|
130
|
+
if (typeof options === 'string') {
|
|
131
|
+
throw new TypeError('Passing baseUrl as a string is no longer supported. Use an options object instead: getAuthToken(args, { baseUrl })');
|
|
132
|
+
}
|
|
133
|
+
return requestAuthToken(Object.assign({}, args), 'Authentication token exchange failed.', options);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Exchanges a refresh token for a new access token using the OAuth2
|
|
137
|
+
* `refresh_token` grant.
|
|
138
|
+
*
|
|
139
|
+
* Only relevant for OAuth apps that have refresh tokens enabled (the default for
|
|
140
|
+
* new apps). Apps without them never receive a `refreshToken` from
|
|
141
|
+
* {@link getAuthToken}, so they have nothing to pass here — their access tokens
|
|
142
|
+
* are long-lived instead.
|
|
143
|
+
*
|
|
144
|
+
* Omit `clientSecret` for public clients registered with
|
|
145
|
+
* `tokenEndpointAuthMethod: 'none'`.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const { accessToken, refreshToken } = await refreshAuthToken({
|
|
150
|
+
* clientId: 'your-client-id',
|
|
151
|
+
* clientSecret: 'your-client-secret',
|
|
152
|
+
* refreshToken: storedRefreshToken,
|
|
153
|
+
* })
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @returns The refreshed token. The server rotates the refresh token, so the
|
|
157
|
+
* response carries a new `refreshToken` — persist it in place of the old one.
|
|
158
|
+
* @throws {@link TodoistRequestError} If the refresh fails
|
|
159
|
+
*/
|
|
160
|
+
export async function refreshAuthToken(args, options) {
|
|
161
|
+
const payload = {
|
|
162
|
+
clientId: args.clientId,
|
|
163
|
+
refreshToken: args.refreshToken,
|
|
164
|
+
grantType: 'refresh_token',
|
|
165
|
+
};
|
|
166
|
+
if (args.clientSecret !== undefined) {
|
|
167
|
+
payload.clientSecret = args.clientSecret;
|
|
127
168
|
}
|
|
169
|
+
return requestAuthToken(payload, 'Authentication token refresh failed.', options);
|
|
128
170
|
}
|
|
129
171
|
/**
|
|
130
172
|
* Revokes a token using the RFC 7009 OAuth 2.0 Token Revocation standard.
|
|
@@ -74,6 +74,31 @@ export type AuthTokenRequestArgs = {
|
|
|
74
74
|
export type AuthTokenResponse = {
|
|
75
75
|
accessToken: string;
|
|
76
76
|
tokenType: string;
|
|
77
|
+
/**
|
|
78
|
+
* Refresh token for use with {@link refreshAuthToken}.
|
|
79
|
+
*
|
|
80
|
+
* Only present when the OAuth app has refresh tokens enabled — the default
|
|
81
|
+
* for new apps, and immutable once on. Apps without it receive a long-lived
|
|
82
|
+
* access token and no refresh token.
|
|
83
|
+
*/
|
|
84
|
+
refreshToken?: string;
|
|
85
|
+
/** Access token lifetime in seconds. */
|
|
86
|
+
expiresIn?: number;
|
|
87
|
+
/** Space-separated granted scopes, when returned. */
|
|
88
|
+
scope?: string;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Parameters required to exchange a refresh token for a new access token.
|
|
92
|
+
* @see https://developer.todoist.com/api/v1/#tag/Authorization/OAuth
|
|
93
|
+
*/
|
|
94
|
+
export type RefreshTokenRequestArgs = {
|
|
95
|
+
clientId: string;
|
|
96
|
+
/**
|
|
97
|
+
* Client secret. Required for confidential clients; omit for public clients
|
|
98
|
+
* registered with `tokenEndpointAuthMethod: 'none'`.
|
|
99
|
+
*/
|
|
100
|
+
clientSecret?: string;
|
|
101
|
+
refreshToken: string;
|
|
77
102
|
};
|
|
78
103
|
/**
|
|
79
104
|
* Parameters required to revoke a token using RFC 7009 compliant endpoint.
|
|
@@ -148,22 +173,33 @@ export declare function getAuthorizationUrl({ clientId, permissions, state, base
|
|
|
148
173
|
state: string;
|
|
149
174
|
baseUrl?: string;
|
|
150
175
|
}): string;
|
|
176
|
+
export declare function getAuthToken(args: AuthTokenRequestArgs, options?: AuthOptions): Promise<AuthTokenResponse>;
|
|
151
177
|
/**
|
|
152
|
-
* Exchanges
|
|
178
|
+
* Exchanges a refresh token for a new access token using the OAuth2
|
|
179
|
+
* `refresh_token` grant.
|
|
180
|
+
*
|
|
181
|
+
* Only relevant for OAuth apps that have refresh tokens enabled (the default for
|
|
182
|
+
* new apps). Apps without them never receive a `refreshToken` from
|
|
183
|
+
* {@link getAuthToken}, so they have nothing to pass here — their access tokens
|
|
184
|
+
* are long-lived instead.
|
|
185
|
+
*
|
|
186
|
+
* Omit `clientSecret` for public clients registered with
|
|
187
|
+
* `tokenEndpointAuthMethod: 'none'`.
|
|
153
188
|
*
|
|
154
189
|
* @example
|
|
155
190
|
* ```typescript
|
|
156
|
-
* const { accessToken } = await
|
|
191
|
+
* const { accessToken, refreshToken } = await refreshAuthToken({
|
|
157
192
|
* clientId: 'your-client-id',
|
|
158
193
|
* clientSecret: 'your-client-secret',
|
|
159
|
-
*
|
|
194
|
+
* refreshToken: storedRefreshToken,
|
|
160
195
|
* })
|
|
161
196
|
* ```
|
|
162
197
|
*
|
|
163
|
-
* @returns The
|
|
164
|
-
*
|
|
198
|
+
* @returns The refreshed token. The server rotates the refresh token, so the
|
|
199
|
+
* response carries a new `refreshToken` — persist it in place of the old one.
|
|
200
|
+
* @throws {@link TodoistRequestError} If the refresh fails
|
|
165
201
|
*/
|
|
166
|
-
export declare function
|
|
202
|
+
export declare function refreshAuthToken(args: RefreshTokenRequestArgs, options?: AuthOptions): Promise<AuthTokenResponse>;
|
|
167
203
|
/**
|
|
168
204
|
* Revokes a token using the RFC 7009 OAuth 2.0 Token Revocation standard.
|
|
169
205
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/todoist-sdk",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.5.0",
|
|
4
4
|
"description": "The official TypeScript SDK for the Todoist REST API.",
|
|
5
5
|
"author": "Doist developers",
|
|
6
6
|
"repository": "https://github.com/Doist/todoist-sdk-typescript",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"camelcase": "6.3.0",
|
|
54
54
|
"emoji-regex": "10.6.0",
|
|
55
|
-
"form-data": "4.0.
|
|
55
|
+
"form-data": "4.0.6",
|
|
56
56
|
"ts-custom-error": "^3.2.0",
|
|
57
57
|
"undici": "7.24.8",
|
|
58
58
|
"uuid": "11.1.1",
|