@heyatom/bastion-client 0.4.0 → 0.6.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/dist/cjs/core/api-client.d.ts +8 -1
- package/dist/cjs/core/api-client.js +9 -0
- package/dist/cjs/core/api-types.d.ts +6 -0
- package/dist/cjs/core/errors.d.ts +2 -0
- package/dist/cjs/core/errors.js +3 -0
- package/dist/esm/core/api-client.d.ts +8 -1
- package/dist/esm/core/api-client.js +9 -0
- package/dist/esm/core/api-types.d.ts +6 -0
- package/dist/esm/core/errors.d.ts +2 -0
- package/dist/esm/core/errors.js +3 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BastionHttp } from './bastion-http.js';
|
|
2
2
|
import { type BastionLogger } from './types.js';
|
|
3
|
-
import type { BastionAgeVerificationResult, BastionAuthMethods, BastionClientContext, BastionEmailChangeRequest, BastionLinkTicket, BastionLoginResult, BastionMe, BastionProfile, BastionProfileUpdate, BastionRegisterInput, BastionSession, BastionSocialAccount, BastionTokenPair, BastionTwoFactorConfirmed, BastionTwoFactorSetup, BastionTwoFactorStatus, BastionUserAuditEventsPage } from './api-types.js';
|
|
3
|
+
import type { BastionAgeVerificationResult, BastionAuthMethods, BastionClientContext, BastionEmailChangeRequest, BastionLinkCompleted, BastionLinkTicket, BastionLoginResult, BastionMe, BastionProfile, BastionProfileUpdate, BastionRegisterInput, BastionSession, BastionSocialAccount, BastionTokenPair, BastionTwoFactorConfirmed, BastionTwoFactorSetup, BastionTwoFactorStatus, BastionUserAuditEventsPage } from './api-types.js';
|
|
4
4
|
export interface BastionApiClientOptions {
|
|
5
5
|
http: BastionHttp;
|
|
6
6
|
/** This app's slug — sent as `appSlug` on every user-facing call. */
|
|
@@ -75,6 +75,13 @@ export declare class BastionApiClient {
|
|
|
75
75
|
}>;
|
|
76
76
|
listSocialAccounts(accessToken: string): Promise<BastionSocialAccount[]>;
|
|
77
77
|
createSocialAccountLinkTicket(accessToken: string, provider: string): Promise<BastionLinkTicket>;
|
|
78
|
+
/**
|
|
79
|
+
* Second step of a link: spends the one-time `linkCode` Bastion put on the
|
|
80
|
+
* redirect to `/auth/callback`. Pass the signed-in user's own token — Bastion
|
|
81
|
+
* links only if its `sub` is the user the ticket was issued to (403
|
|
82
|
+
* `OAUTH_LINK_USER_MISMATCH` otherwise; the code is spent either way).
|
|
83
|
+
*/
|
|
84
|
+
completeSocialAccountLink(accessToken: string, code: string): Promise<BastionLinkCompleted>;
|
|
78
85
|
unlinkSocialAccount(accessToken: string, provider: string): Promise<void>;
|
|
79
86
|
updateSocialAccountSyncAvatar(accessToken: string, provider: string, syncAvatar: boolean): Promise<BastionSocialAccount>;
|
|
80
87
|
resendVerification(email: string, ctx?: BastionClientContext): Promise<void>;
|
|
@@ -156,6 +156,15 @@ class BastionApiClient {
|
|
|
156
156
|
createSocialAccountLinkTicket(accessToken, provider) {
|
|
157
157
|
return this.call('POST', `/auth/me/social-accounts/${encodeURIComponent(provider)}/link-ticket`, undefined, { token: accessToken });
|
|
158
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Second step of a link: spends the one-time `linkCode` Bastion put on the
|
|
161
|
+
* redirect to `/auth/callback`. Pass the signed-in user's own token — Bastion
|
|
162
|
+
* links only if its `sub` is the user the ticket was issued to (403
|
|
163
|
+
* `OAUTH_LINK_USER_MISMATCH` otherwise; the code is spent either way).
|
|
164
|
+
*/
|
|
165
|
+
completeSocialAccountLink(accessToken, code) {
|
|
166
|
+
return this.call('POST', '/auth/me/social-accounts/link/complete', { code }, { token: accessToken });
|
|
167
|
+
}
|
|
159
168
|
unlinkSocialAccount(accessToken, provider) {
|
|
160
169
|
return this.call('DELETE', `/auth/me/social-accounts/${encodeURIComponent(provider)}`, undefined, { token: accessToken });
|
|
161
170
|
}
|
|
@@ -96,6 +96,8 @@ export interface BastionClientContext {
|
|
|
96
96
|
}
|
|
97
97
|
export interface BastionSocialAccount {
|
|
98
98
|
provider: string;
|
|
99
|
+
/** The provider's own id for the account (e.g. the Twitch user id), as verified by the OAuth flow. */
|
|
100
|
+
providerId: string;
|
|
99
101
|
username: string | null;
|
|
100
102
|
email: string | null;
|
|
101
103
|
avatar: string | null;
|
|
@@ -119,6 +121,10 @@ export interface BastionAuthMethods {
|
|
|
119
121
|
export interface BastionLinkTicket {
|
|
120
122
|
url: string;
|
|
121
123
|
}
|
|
124
|
+
/** `POST /auth/me/social-accounts/link/complete` — the provider now linked (lowercase slug). */
|
|
125
|
+
export interface BastionLinkCompleted {
|
|
126
|
+
provider: string;
|
|
127
|
+
}
|
|
122
128
|
/** One row of `GET /auth/me/events` — the user-facing projection, deliberately without `ip`/`userAgent`. */
|
|
123
129
|
export interface BastionUserAuditEvent {
|
|
124
130
|
id: string;
|
|
@@ -29,6 +29,8 @@ export declare const BastionErrorCode: {
|
|
|
29
29
|
readonly OAUTH_EMAIL_UNVERIFIED: "OAUTH_EMAIL_UNVERIFIED";
|
|
30
30
|
readonly OAUTH_EMAIL_REQUIRED: "OAUTH_EMAIL_REQUIRED";
|
|
31
31
|
readonly OAUTH_NO_ACCOUNT: "OAUTH_NO_ACCOUNT";
|
|
32
|
+
/** `completeSocialAccountLink` called with a token that is not the link ticket's owner. The code is spent; start the link again. */
|
|
33
|
+
readonly OAUTH_LINK_USER_MISMATCH: "OAUTH_LINK_USER_MISMATCH";
|
|
32
34
|
readonly UNAVAILABLE: "UNAVAILABLE";
|
|
33
35
|
readonly UPSTREAM: "UPSTREAM";
|
|
34
36
|
};
|
package/dist/cjs/core/errors.js
CHANGED
|
@@ -37,6 +37,8 @@ exports.BastionErrorCode = {
|
|
|
37
37
|
OAUTH_EMAIL_UNVERIFIED: 'OAUTH_EMAIL_UNVERIFIED',
|
|
38
38
|
OAUTH_EMAIL_REQUIRED: 'OAUTH_EMAIL_REQUIRED',
|
|
39
39
|
OAUTH_NO_ACCOUNT: 'OAUTH_NO_ACCOUNT',
|
|
40
|
+
/** `completeSocialAccountLink` called with a token that is not the link ticket's owner. The code is spent; start the link again. */
|
|
41
|
+
OAUTH_LINK_USER_MISMATCH: 'OAUTH_LINK_USER_MISMATCH',
|
|
40
42
|
UNAVAILABLE: 'UNAVAILABLE',
|
|
41
43
|
UPSTREAM: 'UPSTREAM',
|
|
42
44
|
};
|
|
@@ -49,6 +51,7 @@ const CODE_MESSAGES = new Set([
|
|
|
49
51
|
exports.BastionErrorCode.OAUTH_EMAIL_UNVERIFIED,
|
|
50
52
|
exports.BastionErrorCode.OAUTH_EMAIL_REQUIRED,
|
|
51
53
|
exports.BastionErrorCode.OAUTH_NO_ACCOUNT,
|
|
54
|
+
exports.BastionErrorCode.OAUTH_LINK_USER_MISMATCH,
|
|
52
55
|
]);
|
|
53
56
|
const APP_CONTEXT_RE = /app not found|tenant not found|tenant is inactive|no tenants configured|tenantslug is required/i;
|
|
54
57
|
const OAUTH_CODE_RE = /already used code|code expired|code\/app mismatch/i;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BastionHttp } from './bastion-http.js';
|
|
2
2
|
import { type BastionLogger } from './types.js';
|
|
3
|
-
import type { BastionAgeVerificationResult, BastionAuthMethods, BastionClientContext, BastionEmailChangeRequest, BastionLinkTicket, BastionLoginResult, BastionMe, BastionProfile, BastionProfileUpdate, BastionRegisterInput, BastionSession, BastionSocialAccount, BastionTokenPair, BastionTwoFactorConfirmed, BastionTwoFactorSetup, BastionTwoFactorStatus, BastionUserAuditEventsPage } from './api-types.js';
|
|
3
|
+
import type { BastionAgeVerificationResult, BastionAuthMethods, BastionClientContext, BastionEmailChangeRequest, BastionLinkCompleted, BastionLinkTicket, BastionLoginResult, BastionMe, BastionProfile, BastionProfileUpdate, BastionRegisterInput, BastionSession, BastionSocialAccount, BastionTokenPair, BastionTwoFactorConfirmed, BastionTwoFactorSetup, BastionTwoFactorStatus, BastionUserAuditEventsPage } from './api-types.js';
|
|
4
4
|
export interface BastionApiClientOptions {
|
|
5
5
|
http: BastionHttp;
|
|
6
6
|
/** This app's slug — sent as `appSlug` on every user-facing call. */
|
|
@@ -75,6 +75,13 @@ export declare class BastionApiClient {
|
|
|
75
75
|
}>;
|
|
76
76
|
listSocialAccounts(accessToken: string): Promise<BastionSocialAccount[]>;
|
|
77
77
|
createSocialAccountLinkTicket(accessToken: string, provider: string): Promise<BastionLinkTicket>;
|
|
78
|
+
/**
|
|
79
|
+
* Second step of a link: spends the one-time `linkCode` Bastion put on the
|
|
80
|
+
* redirect to `/auth/callback`. Pass the signed-in user's own token — Bastion
|
|
81
|
+
* links only if its `sub` is the user the ticket was issued to (403
|
|
82
|
+
* `OAUTH_LINK_USER_MISMATCH` otherwise; the code is spent either way).
|
|
83
|
+
*/
|
|
84
|
+
completeSocialAccountLink(accessToken: string, code: string): Promise<BastionLinkCompleted>;
|
|
78
85
|
unlinkSocialAccount(accessToken: string, provider: string): Promise<void>;
|
|
79
86
|
updateSocialAccountSyncAvatar(accessToken: string, provider: string, syncAvatar: boolean): Promise<BastionSocialAccount>;
|
|
80
87
|
resendVerification(email: string, ctx?: BastionClientContext): Promise<void>;
|
|
@@ -153,6 +153,15 @@ export class BastionApiClient {
|
|
|
153
153
|
createSocialAccountLinkTicket(accessToken, provider) {
|
|
154
154
|
return this.call('POST', `/auth/me/social-accounts/${encodeURIComponent(provider)}/link-ticket`, undefined, { token: accessToken });
|
|
155
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Second step of a link: spends the one-time `linkCode` Bastion put on the
|
|
158
|
+
* redirect to `/auth/callback`. Pass the signed-in user's own token — Bastion
|
|
159
|
+
* links only if its `sub` is the user the ticket was issued to (403
|
|
160
|
+
* `OAUTH_LINK_USER_MISMATCH` otherwise; the code is spent either way).
|
|
161
|
+
*/
|
|
162
|
+
completeSocialAccountLink(accessToken, code) {
|
|
163
|
+
return this.call('POST', '/auth/me/social-accounts/link/complete', { code }, { token: accessToken });
|
|
164
|
+
}
|
|
156
165
|
unlinkSocialAccount(accessToken, provider) {
|
|
157
166
|
return this.call('DELETE', `/auth/me/social-accounts/${encodeURIComponent(provider)}`, undefined, { token: accessToken });
|
|
158
167
|
}
|
|
@@ -96,6 +96,8 @@ export interface BastionClientContext {
|
|
|
96
96
|
}
|
|
97
97
|
export interface BastionSocialAccount {
|
|
98
98
|
provider: string;
|
|
99
|
+
/** The provider's own id for the account (e.g. the Twitch user id), as verified by the OAuth flow. */
|
|
100
|
+
providerId: string;
|
|
99
101
|
username: string | null;
|
|
100
102
|
email: string | null;
|
|
101
103
|
avatar: string | null;
|
|
@@ -119,6 +121,10 @@ export interface BastionAuthMethods {
|
|
|
119
121
|
export interface BastionLinkTicket {
|
|
120
122
|
url: string;
|
|
121
123
|
}
|
|
124
|
+
/** `POST /auth/me/social-accounts/link/complete` — the provider now linked (lowercase slug). */
|
|
125
|
+
export interface BastionLinkCompleted {
|
|
126
|
+
provider: string;
|
|
127
|
+
}
|
|
122
128
|
/** One row of `GET /auth/me/events` — the user-facing projection, deliberately without `ip`/`userAgent`. */
|
|
123
129
|
export interface BastionUserAuditEvent {
|
|
124
130
|
id: string;
|
|
@@ -29,6 +29,8 @@ export declare const BastionErrorCode: {
|
|
|
29
29
|
readonly OAUTH_EMAIL_UNVERIFIED: "OAUTH_EMAIL_UNVERIFIED";
|
|
30
30
|
readonly OAUTH_EMAIL_REQUIRED: "OAUTH_EMAIL_REQUIRED";
|
|
31
31
|
readonly OAUTH_NO_ACCOUNT: "OAUTH_NO_ACCOUNT";
|
|
32
|
+
/** `completeSocialAccountLink` called with a token that is not the link ticket's owner. The code is spent; start the link again. */
|
|
33
|
+
readonly OAUTH_LINK_USER_MISMATCH: "OAUTH_LINK_USER_MISMATCH";
|
|
32
34
|
readonly UNAVAILABLE: "UNAVAILABLE";
|
|
33
35
|
readonly UPSTREAM: "UPSTREAM";
|
|
34
36
|
};
|
package/dist/esm/core/errors.js
CHANGED
|
@@ -32,6 +32,8 @@ export const BastionErrorCode = {
|
|
|
32
32
|
OAUTH_EMAIL_UNVERIFIED: 'OAUTH_EMAIL_UNVERIFIED',
|
|
33
33
|
OAUTH_EMAIL_REQUIRED: 'OAUTH_EMAIL_REQUIRED',
|
|
34
34
|
OAUTH_NO_ACCOUNT: 'OAUTH_NO_ACCOUNT',
|
|
35
|
+
/** `completeSocialAccountLink` called with a token that is not the link ticket's owner. The code is spent; start the link again. */
|
|
36
|
+
OAUTH_LINK_USER_MISMATCH: 'OAUTH_LINK_USER_MISMATCH',
|
|
35
37
|
UNAVAILABLE: 'UNAVAILABLE',
|
|
36
38
|
UPSTREAM: 'UPSTREAM',
|
|
37
39
|
};
|
|
@@ -44,6 +46,7 @@ const CODE_MESSAGES = new Set([
|
|
|
44
46
|
BastionErrorCode.OAUTH_EMAIL_UNVERIFIED,
|
|
45
47
|
BastionErrorCode.OAUTH_EMAIL_REQUIRED,
|
|
46
48
|
BastionErrorCode.OAUTH_NO_ACCOUNT,
|
|
49
|
+
BastionErrorCode.OAUTH_LINK_USER_MISMATCH,
|
|
47
50
|
]);
|
|
48
51
|
const APP_CONTEXT_RE = /app not found|tenant not found|tenant is inactive|no tenants configured|tenantslug is required/i;
|
|
49
52
|
const OAUTH_CODE_RE = /already used code|code expired|code\/app mismatch/i;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyatom/bastion-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Consumer SDK for the Bastion identity provider: JWKS verification, service-client auth, audit writes, NestJS guards",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Andrea Tombolato <hey@heyatom.dev> (https://heyatom.dev)",
|