@ticketlayer/backstage 3.1.0 → 4.1.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 +167 -0
- package/dist/auth/dpop.d.ts +27 -0
- package/dist/auth/dpop.d.ts.map +1 -0
- package/dist/auth/dpop.js +51 -0
- package/dist/auth/encoding.d.ts +8 -0
- package/dist/auth/encoding.d.ts.map +1 -0
- package/dist/auth/encoding.js +53 -0
- package/dist/auth/index.d.ts +152 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +332 -0
- package/dist/auth/pkce.d.ts +8 -0
- package/dist/auth/pkce.d.ts.map +1 -0
- package/dist/auth/pkce.js +15 -0
- package/dist/client.d.ts +52 -30
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +101 -49
- package/dist/generated/types.d.ts +155 -69
- package/dist/generated/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthSession = exports.BackstageAuth = exports.ACCESS_TOKEN_TYPE = exports.REFRESH_TOKEN_TYPE = exports.TOKEN_EXCHANGE_GRANT = exports.jwkThumbprint = exports.dpopProof = exports.dpopHtu = exports.createDpopKey = exports.pkceChallenge = exports.pkce = void 0;
|
|
4
|
+
exports.createAuth = createAuth;
|
|
5
|
+
const client_1 = require("../client");
|
|
6
|
+
const dpop_1 = require("./dpop");
|
|
7
|
+
const encoding_1 = require("./encoding");
|
|
8
|
+
const pkce_1 = require("./pkce");
|
|
9
|
+
var pkce_2 = require("./pkce");
|
|
10
|
+
Object.defineProperty(exports, "pkce", { enumerable: true, get: function () { return pkce_2.pkce; } });
|
|
11
|
+
Object.defineProperty(exports, "pkceChallenge", { enumerable: true, get: function () { return pkce_2.pkceChallenge; } });
|
|
12
|
+
var dpop_2 = require("./dpop");
|
|
13
|
+
Object.defineProperty(exports, "createDpopKey", { enumerable: true, get: function () { return dpop_2.createDpopKey; } });
|
|
14
|
+
Object.defineProperty(exports, "dpopHtu", { enumerable: true, get: function () { return dpop_2.dpopHtu; } });
|
|
15
|
+
Object.defineProperty(exports, "dpopProof", { enumerable: true, get: function () { return dpop_2.dpopProof; } });
|
|
16
|
+
Object.defineProperty(exports, "jwkThumbprint", { enumerable: true, get: function () { return dpop_2.jwkThumbprint; } });
|
|
17
|
+
exports.TOKEN_EXCHANGE_GRANT = 'urn:ietf:params:oauth:grant-type:token-exchange';
|
|
18
|
+
exports.REFRESH_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:refresh_token';
|
|
19
|
+
exports.ACCESS_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:access_token';
|
|
20
|
+
function assertServerSide() {
|
|
21
|
+
const g = globalThis;
|
|
22
|
+
if (typeof g.window !== 'undefined' && typeof g.document !== 'undefined') {
|
|
23
|
+
throw new Error('clientSecret is for server use only (the shell BFF). Never ship a client secret to a browser; public clients use PKCE and DPoP.');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const formEncode = (value) => encodeURIComponent(value).replace(/%20/g, '+');
|
|
27
|
+
function organisationsFrom(idToken) {
|
|
28
|
+
if (!idToken)
|
|
29
|
+
return undefined;
|
|
30
|
+
const claim = (0, encoding_1.decodeJwtPayload)(idToken)?.organisations;
|
|
31
|
+
if (!Array.isArray(claim))
|
|
32
|
+
return undefined;
|
|
33
|
+
return claim
|
|
34
|
+
.filter((o) => !!o && typeof o === 'object' && typeof o.slug === 'string')
|
|
35
|
+
.map((o) => ({ id: String(o.id), slug: o.slug, name: String(o.name) }));
|
|
36
|
+
}
|
|
37
|
+
function toTokenSet(body, previous) {
|
|
38
|
+
const accessToken = body.access_token;
|
|
39
|
+
if (typeof accessToken !== 'string' || !accessToken) {
|
|
40
|
+
throw new client_1.BackstageAPIError('The token endpoint answered no access token', 'INVALID_TOKEN_RESPONSE', 502);
|
|
41
|
+
}
|
|
42
|
+
const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : 0;
|
|
43
|
+
const idToken = typeof body.id_token === 'string' ? body.id_token : undefined;
|
|
44
|
+
let organisation;
|
|
45
|
+
if ('org_slug' in body) {
|
|
46
|
+
organisation =
|
|
47
|
+
typeof body.org_slug === 'string'
|
|
48
|
+
? { slug: body.org_slug, name: typeof body.org_name === 'string' ? body.org_name : null }
|
|
49
|
+
: null;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
organisation = previous?.organisation ?? null;
|
|
53
|
+
}
|
|
54
|
+
const tokens = {
|
|
55
|
+
accessToken,
|
|
56
|
+
tokenType: typeof body.token_type === 'string' ? body.token_type : 'Bearer',
|
|
57
|
+
expiresIn,
|
|
58
|
+
expiresAt: Date.now() + expiresIn * 1000,
|
|
59
|
+
organisation,
|
|
60
|
+
};
|
|
61
|
+
const refreshToken = typeof body.refresh_token === 'string' ? body.refresh_token : previous?.refreshToken;
|
|
62
|
+
if (refreshToken)
|
|
63
|
+
tokens.refreshToken = refreshToken;
|
|
64
|
+
if (typeof body.scope === 'string')
|
|
65
|
+
tokens.scope = body.scope;
|
|
66
|
+
if (idToken)
|
|
67
|
+
tokens.idToken = idToken;
|
|
68
|
+
const organisations = organisationsFrom(idToken) ?? previous?.organisations;
|
|
69
|
+
if (organisations)
|
|
70
|
+
tokens.organisations = organisations;
|
|
71
|
+
return tokens;
|
|
72
|
+
}
|
|
73
|
+
class BackstageAuth {
|
|
74
|
+
constructor(config) {
|
|
75
|
+
this.pkce = pkce_1.pkce;
|
|
76
|
+
this.pkceChallenge = pkce_1.pkceChallenge;
|
|
77
|
+
this.createDpopKey = dpop_1.createDpopKey;
|
|
78
|
+
this.dpopProof = dpop_1.dpopProof;
|
|
79
|
+
if (!config || !config.issuerUrl)
|
|
80
|
+
throw new Error('createAuth needs issuerUrl (https://auth.<domain>)');
|
|
81
|
+
this.issuerUrl = config.issuerUrl.replace(/\/$/, '');
|
|
82
|
+
this.tokenEndpoint = config.tokenEndpoint ?? `${this.issuerUrl}/oauth/token`;
|
|
83
|
+
this.clientId = config.clientId;
|
|
84
|
+
this.debugConfig =
|
|
85
|
+
config.debug === true
|
|
86
|
+
? { logRequests: true, logResponses: true, logErrors: true }
|
|
87
|
+
: config.debug && typeof config.debug === 'object'
|
|
88
|
+
? config.debug
|
|
89
|
+
: null;
|
|
90
|
+
this.logger = this.debugConfig
|
|
91
|
+
? this.debugConfig.logger ?? {
|
|
92
|
+
log: (...args) => console.log('[BackstageSDK]', ...args),
|
|
93
|
+
error: (...args) => console.error('[BackstageSDK]', ...args),
|
|
94
|
+
}
|
|
95
|
+
: null;
|
|
96
|
+
this.issuer = new client_1.BackstageClient({
|
|
97
|
+
baseUrl: this.issuerUrl,
|
|
98
|
+
authMode: 'bearer',
|
|
99
|
+
debug: config.debug,
|
|
100
|
+
headers: config.headers,
|
|
101
|
+
credentials: config.credentials,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async start(input) {
|
|
105
|
+
const answer = (await this.issuer.identity.start({
|
|
106
|
+
client_id: this.clientIdOf(input.clientId),
|
|
107
|
+
code_challenge: input.codeChallenge,
|
|
108
|
+
code_challenge_method: 'S256',
|
|
109
|
+
email: input.email,
|
|
110
|
+
redirect_uri: input.redirectUri,
|
|
111
|
+
scope: input.scope,
|
|
112
|
+
dpop_jkt: input.dpop?.thumbprint,
|
|
113
|
+
organisation: input.organisation,
|
|
114
|
+
}));
|
|
115
|
+
const result = {
|
|
116
|
+
method: answer.method,
|
|
117
|
+
authSession: answer.authSession,
|
|
118
|
+
alternatives: answer.alternatives ?? [],
|
|
119
|
+
expiresIn: answer.expiresIn,
|
|
120
|
+
};
|
|
121
|
+
if (answer.redirectUrl)
|
|
122
|
+
result.redirectUrl = answer.redirectUrl;
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
async password(input) {
|
|
126
|
+
return this.step(await this.issuer.identity.password({ authSession: input.authSession, password: input.password }));
|
|
127
|
+
}
|
|
128
|
+
async sendEmailCode(input) {
|
|
129
|
+
const answer = await this.issuer.identity.emailCode({ authSession: input.authSession });
|
|
130
|
+
if (answer.status !== 'code_sent') {
|
|
131
|
+
throw new client_1.BackstageAPIError('Expected code_sent from /auth/email-code', 'UNEXPECTED_RESPONSE', 502);
|
|
132
|
+
}
|
|
133
|
+
return { status: 'code_sent' };
|
|
134
|
+
}
|
|
135
|
+
async emailCode(input) {
|
|
136
|
+
if (!input.code)
|
|
137
|
+
throw new Error('emailCode needs the code; use sendEmailCode to have one sent');
|
|
138
|
+
return this.step(await this.issuer.identity.emailCode({ authSession: input.authSession, code: input.code }));
|
|
139
|
+
}
|
|
140
|
+
async mfa(input) {
|
|
141
|
+
return this.step(await this.issuer.identity.mfa({ authSession: input.authSession, code: input.code }));
|
|
142
|
+
}
|
|
143
|
+
step(answer) {
|
|
144
|
+
if (answer.status === 'code')
|
|
145
|
+
return { status: 'code', code: answer.code, redirectUri: answer.redirectUri };
|
|
146
|
+
if (answer.status === 'mfa_required') {
|
|
147
|
+
return { status: 'mfa_required', authSession: answer.authSession, factors: [...answer.factors] };
|
|
148
|
+
}
|
|
149
|
+
throw new client_1.BackstageAPIError(`Unexpected sign-in answer: ${String(answer.status)}`, 'UNEXPECTED_RESPONSE', 502);
|
|
150
|
+
}
|
|
151
|
+
async exchange(input) {
|
|
152
|
+
const params = { code: input.code, code_verifier: input.codeVerifier };
|
|
153
|
+
if (input.redirectUri)
|
|
154
|
+
params.redirect_uri = input.redirectUri;
|
|
155
|
+
return this.token('authorization_code', params, input);
|
|
156
|
+
}
|
|
157
|
+
async refresh(input) {
|
|
158
|
+
return this.token('refresh_token', { refresh_token: input.refreshToken }, input, {
|
|
159
|
+
refreshToken: input.refreshToken,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async selectOrganisation(input) {
|
|
163
|
+
const subject = input.refreshToken ?? input.accessToken;
|
|
164
|
+
if (!subject)
|
|
165
|
+
throw new Error('selectOrganisation needs a refreshToken or an accessToken');
|
|
166
|
+
return this.token(exports.TOKEN_EXCHANGE_GRANT, {
|
|
167
|
+
subject_token: subject,
|
|
168
|
+
subject_token_type: input.refreshToken ? exports.REFRESH_TOKEN_TYPE : exports.ACCESS_TOKEN_TYPE,
|
|
169
|
+
requested_token_type: exports.ACCESS_TOKEN_TYPE,
|
|
170
|
+
organisation_slug: input.slug,
|
|
171
|
+
}, input);
|
|
172
|
+
}
|
|
173
|
+
async token(grantType, params, credentials, previous) {
|
|
174
|
+
const clientId = this.clientIdOf(credentials.clientId);
|
|
175
|
+
const headers = {
|
|
176
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
177
|
+
Accept: 'application/json',
|
|
178
|
+
};
|
|
179
|
+
const form = new URLSearchParams({ grant_type: grantType, ...params });
|
|
180
|
+
if (credentials.clientSecret) {
|
|
181
|
+
assertServerSide();
|
|
182
|
+
headers.Authorization = `Basic ${btoa(`${formEncode(clientId)}:${formEncode(credentials.clientSecret)}`)}`;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
form.set('client_id', clientId);
|
|
186
|
+
}
|
|
187
|
+
const send = async (nonce) => {
|
|
188
|
+
if (credentials.dpop) {
|
|
189
|
+
headers.DPoP = await credentials.dpop.proof({ method: 'POST', url: this.tokenEndpoint, nonce });
|
|
190
|
+
}
|
|
191
|
+
if (this.debugConfig?.logRequests)
|
|
192
|
+
this.logger?.log('→', 'POST', this.tokenEndpoint, { grant_type: grantType });
|
|
193
|
+
return fetch(this.tokenEndpoint, { method: 'POST', headers, body: form.toString() });
|
|
194
|
+
};
|
|
195
|
+
let response = await send();
|
|
196
|
+
let body = (await response.json().catch(() => ({})));
|
|
197
|
+
const nonce = response.headers?.get?.('DPoP-Nonce');
|
|
198
|
+
if (!response.ok && body.error === 'use_dpop_nonce' && nonce && credentials.dpop) {
|
|
199
|
+
response = await send(nonce);
|
|
200
|
+
body = (await response.json().catch(() => ({})));
|
|
201
|
+
}
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
const code = typeof body.error === 'string' ? body.error : 'HTTP_ERROR';
|
|
204
|
+
const message = typeof body.error_description === 'string' ? body.error_description : `Token request failed (${code})`;
|
|
205
|
+
if (this.debugConfig?.logErrors)
|
|
206
|
+
this.logger?.error('✗', 'POST', this.tokenEndpoint, { status: response.status, code });
|
|
207
|
+
throw new client_1.BackstageAPIError(message, code, response.status, {
|
|
208
|
+
requestId: response.headers?.get?.('x-request-id') ?? undefined,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (this.debugConfig?.logResponses) {
|
|
212
|
+
this.logger?.log('←', 'POST', this.tokenEndpoint, { status: response.status, grant_type: grantType });
|
|
213
|
+
}
|
|
214
|
+
return toTokenSet(body, previous);
|
|
215
|
+
}
|
|
216
|
+
clientIdOf(clientId) {
|
|
217
|
+
const id = clientId ?? this.clientId;
|
|
218
|
+
if (!id)
|
|
219
|
+
throw new Error('No clientId: pass one to the call or to createAuth');
|
|
220
|
+
return id;
|
|
221
|
+
}
|
|
222
|
+
createSession(options) {
|
|
223
|
+
return new AuthSession(this, options);
|
|
224
|
+
}
|
|
225
|
+
createClient(options) {
|
|
226
|
+
return this.createSession(options).client;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.BackstageAuth = BackstageAuth;
|
|
230
|
+
class AuthSession {
|
|
231
|
+
constructor(auth, options) {
|
|
232
|
+
this.auth = auth;
|
|
233
|
+
this.options = options;
|
|
234
|
+
this.ended = false;
|
|
235
|
+
if (options.clientSecret)
|
|
236
|
+
assertServerSide();
|
|
237
|
+
this.current = { ...options.tokens };
|
|
238
|
+
this.leewayMs = (options.refreshLeewaySeconds ?? 30) * 1000;
|
|
239
|
+
const { tokens: _tokens, clientId: _clientId, clientSecret: _clientSecret, dpop, onTokens: _onTokens, onSessionEnded: _onSessionEnded, refreshLeewaySeconds: _leeway, organisationSlug, ...config } = options;
|
|
240
|
+
this.client = new client_1.BackstageClient({
|
|
241
|
+
...config,
|
|
242
|
+
authMode: 'bearer',
|
|
243
|
+
accessToken: () => {
|
|
244
|
+
this.refreshAhead();
|
|
245
|
+
return this.current.accessToken;
|
|
246
|
+
},
|
|
247
|
+
organisationSlug: organisationSlug ?? (() => this.current.organisation?.slug ?? undefined),
|
|
248
|
+
onAuthFailure: () => this.refresh(),
|
|
249
|
+
dpop,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
get tokens() {
|
|
253
|
+
return { ...this.current };
|
|
254
|
+
}
|
|
255
|
+
get organisation() {
|
|
256
|
+
return this.current.organisation ?? null;
|
|
257
|
+
}
|
|
258
|
+
get hasEnded() {
|
|
259
|
+
return this.ended;
|
|
260
|
+
}
|
|
261
|
+
refresh() {
|
|
262
|
+
if (this.ended)
|
|
263
|
+
return Promise.resolve(false);
|
|
264
|
+
if (!this.pending) {
|
|
265
|
+
this.pending = this.runRefresh().finally(() => {
|
|
266
|
+
this.pending = undefined;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return this.pending;
|
|
270
|
+
}
|
|
271
|
+
async selectOrganisation(slug) {
|
|
272
|
+
while (this.pending)
|
|
273
|
+
await this.pending.catch(() => false);
|
|
274
|
+
const run = (async () => {
|
|
275
|
+
const next = await this.auth.selectOrganisation({
|
|
276
|
+
slug,
|
|
277
|
+
refreshToken: this.current.refreshToken,
|
|
278
|
+
accessToken: this.current.refreshToken ? undefined : this.current.accessToken,
|
|
279
|
+
clientId: this.options.clientId,
|
|
280
|
+
clientSecret: this.options.clientSecret,
|
|
281
|
+
dpop: this.options.dpop,
|
|
282
|
+
});
|
|
283
|
+
await this.adopt(next);
|
|
284
|
+
return next;
|
|
285
|
+
})();
|
|
286
|
+
this.pending = run.then(() => true, () => false);
|
|
287
|
+
try {
|
|
288
|
+
return await run;
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
this.pending = undefined;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
refreshAhead() {
|
|
295
|
+
if (this.ended || this.pending || !this.current.refreshToken)
|
|
296
|
+
return;
|
|
297
|
+
const { accessToken, expiresAt } = this.current;
|
|
298
|
+
const due = !accessToken || (typeof expiresAt === 'number' && expiresAt - Date.now() < this.leewayMs);
|
|
299
|
+
if (due)
|
|
300
|
+
void this.refresh();
|
|
301
|
+
}
|
|
302
|
+
async runRefresh() {
|
|
303
|
+
const refreshToken = this.current.refreshToken;
|
|
304
|
+
if (!refreshToken)
|
|
305
|
+
return false;
|
|
306
|
+
try {
|
|
307
|
+
const next = await this.auth.refresh({
|
|
308
|
+
refreshToken,
|
|
309
|
+
clientId: this.options.clientId,
|
|
310
|
+
clientSecret: this.options.clientSecret,
|
|
311
|
+
dpop: this.options.dpop,
|
|
312
|
+
});
|
|
313
|
+
await this.adopt({ ...next, organisation: next.organisation ?? this.current.organisation ?? null });
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
if (error instanceof client_1.BackstageAPIError && error.statusCode >= 400 && error.statusCode < 500) {
|
|
318
|
+
this.ended = true;
|
|
319
|
+
await this.options.onSessionEnded?.(error);
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async adopt(next) {
|
|
325
|
+
this.current = { ...next };
|
|
326
|
+
await this.options.onTokens?.({ ...next });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
exports.AuthSession = AuthSession;
|
|
330
|
+
function createAuth(config) {
|
|
331
|
+
return new BackstageAuth(config);
|
|
332
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../src/auth/pkce.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAGD,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrE;AAMD,wBAAsB,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CAG9C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pkceChallenge = pkceChallenge;
|
|
4
|
+
exports.pkce = pkce;
|
|
5
|
+
const encoding_1 = require("./encoding");
|
|
6
|
+
async function pkceChallenge(verifier) {
|
|
7
|
+
if (!/^[A-Za-z0-9\-._~]{43,128}$/.test(verifier)) {
|
|
8
|
+
throw new Error('A PKCE verifier is 43 to 128 characters of [A-Za-z0-9-._~] (RFC 7636 4.1)');
|
|
9
|
+
}
|
|
10
|
+
return (0, encoding_1.base64url)(await (0, encoding_1.sha256)(verifier));
|
|
11
|
+
}
|
|
12
|
+
async function pkce() {
|
|
13
|
+
const verifier = (0, encoding_1.base64url)((0, encoding_1.randomBytes)(32));
|
|
14
|
+
return { verifier, challenge: await pkceChallenge(verifier), method: 'S256' };
|
|
15
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -12,14 +12,15 @@ type CreateTagDefinitionRequest = components['schemas']['CreateTagDefinitionRequ
|
|
|
12
12
|
type CreateUserInvitationRequest = components['schemas']['CreateUserInvitationRequest'];
|
|
13
13
|
type CreateWebhookEndpoint = components['schemas']['CreateWebhookEndpoint'];
|
|
14
14
|
type GrantSalesChannelAccessRequest = components['schemas']['GrantSalesChannelAccessRequest'];
|
|
15
|
-
type LoginRequest = components['schemas']['LoginRequest'];
|
|
16
|
-
type RedeemInvitationRequest = components['schemas']['RedeemInvitationRequest'];
|
|
17
|
-
type RefreshTokenRequest = components['schemas']['RefreshTokenRequest'];
|
|
18
15
|
type SavePaymentMethodRequest = components['schemas']['SavePaymentMethodRequest'];
|
|
19
16
|
type SignInInteractionLogin = components['schemas']['SignInInteractionLogin'];
|
|
20
17
|
type SignInInteractionOrganisation = components['schemas']['SignInInteractionOrganisation'];
|
|
21
18
|
type SignUpRequest = components['schemas']['SignUpRequest'];
|
|
22
19
|
type StaffUnlockRequest = components['schemas']['StaffUnlockRequest'];
|
|
20
|
+
type StartSignInRequest = components['schemas']['StartSignInRequest'];
|
|
21
|
+
type SubmitSignInEmailCodeRequest = components['schemas']['SubmitSignInEmailCodeRequest'];
|
|
22
|
+
type SubmitSignInMfaRequest = components['schemas']['SubmitSignInMfaRequest'];
|
|
23
|
+
type SubmitSignInPasswordRequest = components['schemas']['SubmitSignInPasswordRequest'];
|
|
23
24
|
type UpdateAutoTopupRequest = components['schemas']['UpdateAutoTopupRequest'];
|
|
24
25
|
type UpdateCodeRequest = components['schemas']['UpdateCodeRequest'];
|
|
25
26
|
type UpdateIdentityProviderRequest = components['schemas']['UpdateIdentityProviderRequest'];
|
|
@@ -65,6 +66,15 @@ export interface BackstageClientConfig {
|
|
|
65
66
|
refreshEndpoint?: string;
|
|
66
67
|
organisationSlug?: string | (() => string | undefined);
|
|
67
68
|
headers?: Record<string, string>;
|
|
69
|
+
dpop?: DpopSigner;
|
|
70
|
+
credentials?: RequestCredentials;
|
|
71
|
+
}
|
|
72
|
+
export interface DpopSigner {
|
|
73
|
+
proof(input: {
|
|
74
|
+
method: string;
|
|
75
|
+
url: string;
|
|
76
|
+
accessToken?: string;
|
|
77
|
+
}): Promise<string>;
|
|
68
78
|
}
|
|
69
79
|
export declare const API_DATED_VERSION = "2026-06-13";
|
|
70
80
|
export declare const SDK_CLIENT_HEADER: string;
|
|
@@ -76,8 +86,10 @@ export declare class BackstageClient {
|
|
|
76
86
|
private headers;
|
|
77
87
|
private onTokenRefresh?;
|
|
78
88
|
private onAuthFailure?;
|
|
79
|
-
private refreshEndpoint
|
|
89
|
+
private refreshEndpoint?;
|
|
80
90
|
private organisationSlug?;
|
|
91
|
+
private dpop?;
|
|
92
|
+
private credentials?;
|
|
81
93
|
private debugConfig;
|
|
82
94
|
private logger;
|
|
83
95
|
constructor(config: BackstageClientConfig);
|
|
@@ -91,6 +103,7 @@ export declare class BackstageClient {
|
|
|
91
103
|
private resolveOrgSlug;
|
|
92
104
|
private getOrgFromSubdomain;
|
|
93
105
|
private attemptTokenRefresh;
|
|
106
|
+
private authorise;
|
|
94
107
|
private request;
|
|
95
108
|
identity: {
|
|
96
109
|
getSignInInteraction: (uid: string) => Promise<{
|
|
@@ -130,6 +143,41 @@ export declare class BackstageClient {
|
|
|
130
143
|
status: "continue";
|
|
131
144
|
redirectTo: string;
|
|
132
145
|
}>;
|
|
146
|
+
start: (request: StartSignInRequest) => Promise<{
|
|
147
|
+
authSession: string;
|
|
148
|
+
method: "password";
|
|
149
|
+
alternatives: "email_code"[];
|
|
150
|
+
expiresIn: number;
|
|
151
|
+
}>;
|
|
152
|
+
password: (request: SubmitSignInPasswordRequest) => Promise<{
|
|
153
|
+
status: "code";
|
|
154
|
+
code: string;
|
|
155
|
+
redirectUri: string;
|
|
156
|
+
} | {
|
|
157
|
+
status: "mfa_required";
|
|
158
|
+
authSession: string;
|
|
159
|
+
factors: ("totp" | "email")[];
|
|
160
|
+
}>;
|
|
161
|
+
emailCode: (request: SubmitSignInEmailCodeRequest) => Promise<{
|
|
162
|
+
status: "code";
|
|
163
|
+
code: string;
|
|
164
|
+
redirectUri: string;
|
|
165
|
+
} | {
|
|
166
|
+
status: "mfa_required";
|
|
167
|
+
authSession: string;
|
|
168
|
+
factors: ("totp" | "email")[];
|
|
169
|
+
} | {
|
|
170
|
+
status: "code_sent";
|
|
171
|
+
}>;
|
|
172
|
+
mfa: (request: SubmitSignInMfaRequest) => Promise<{
|
|
173
|
+
status: "code";
|
|
174
|
+
code: string;
|
|
175
|
+
redirectUri: string;
|
|
176
|
+
} | {
|
|
177
|
+
status: "mfa_required";
|
|
178
|
+
authSession: string;
|
|
179
|
+
factors: ("totp" | "email")[];
|
|
180
|
+
}>;
|
|
133
181
|
};
|
|
134
182
|
identityProviders: {
|
|
135
183
|
discover: (options?: {
|
|
@@ -235,32 +283,6 @@ export declare class BackstageClient {
|
|
|
235
283
|
versions: components["schemas"]["ApiVersion"][];
|
|
236
284
|
}>;
|
|
237
285
|
};
|
|
238
|
-
authentication: {
|
|
239
|
-
login: (request: LoginRequest) => Promise<{
|
|
240
|
-
accessToken: string;
|
|
241
|
-
refreshToken: string;
|
|
242
|
-
user: {
|
|
243
|
-
id: string;
|
|
244
|
-
email: string;
|
|
245
|
-
firstName?: string;
|
|
246
|
-
lastName?: string;
|
|
247
|
-
};
|
|
248
|
-
}>;
|
|
249
|
-
refresh: (request: RefreshTokenRequest) => Promise<{
|
|
250
|
-
accessToken: string;
|
|
251
|
-
refreshToken: string;
|
|
252
|
-
}>;
|
|
253
|
-
redeem: (request: RedeemInvitationRequest) => Promise<{
|
|
254
|
-
accessToken: string;
|
|
255
|
-
refreshToken: string;
|
|
256
|
-
user: {
|
|
257
|
-
id: string;
|
|
258
|
-
email: string;
|
|
259
|
-
firstName?: string;
|
|
260
|
-
lastName?: string;
|
|
261
|
-
};
|
|
262
|
-
}>;
|
|
263
|
-
};
|
|
264
286
|
signup: {
|
|
265
287
|
signUp: (request: SignUpRequest) => Promise<{
|
|
266
288
|
organisation: {
|