@databricks/sdk-auth 0.0.0-dev → 0.1.0-dev.2

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.
Files changed (99) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +11 -1
  3. package/dist/auth.d.ts +81 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +47 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/credentials/default/chain.d.ts +28 -0
  8. package/dist/credentials/default/chain.d.ts.map +1 -0
  9. package/dist/credentials/default/chain.js +62 -0
  10. package/dist/credentials/default/chain.js.map +1 -0
  11. package/dist/credentials/default/default-credentials.d.ts +25 -0
  12. package/dist/credentials/default/default-credentials.d.ts.map +1 -0
  13. package/dist/credentials/default/default-credentials.js +23 -0
  14. package/dist/credentials/default/default-credentials.js.map +1 -0
  15. package/dist/credentials/default/errors.d.ts +13 -0
  16. package/dist/credentials/default/errors.d.ts.map +1 -0
  17. package/dist/credentials/default/errors.js +15 -0
  18. package/dist/credentials/default/errors.js.map +1 -0
  19. package/dist/credentials/default/u2m-strategy.d.ts +9 -0
  20. package/dist/credentials/default/u2m-strategy.d.ts.map +1 -0
  21. package/dist/credentials/default/u2m-strategy.js +20 -0
  22. package/dist/credentials/default/u2m-strategy.js.map +1 -0
  23. package/dist/credentials/errors.d.ts +28 -0
  24. package/dist/credentials/errors.d.ts.map +1 -0
  25. package/dist/credentials/errors.js +32 -0
  26. package/dist/credentials/errors.js.map +1 -0
  27. package/dist/credentials/host-metadata.d.ts +45 -0
  28. package/dist/credentials/host-metadata.d.ts.map +1 -0
  29. package/dist/credentials/host-metadata.js +122 -0
  30. package/dist/credentials/host-metadata.js.map +1 -0
  31. package/dist/credentials/index.browser.d.ts +11 -0
  32. package/dist/credentials/index.browser.d.ts.map +1 -0
  33. package/dist/credentials/index.browser.js +9 -0
  34. package/dist/credentials/index.browser.js.map +1 -0
  35. package/dist/credentials/index.d.ts +14 -0
  36. package/dist/credentials/index.d.ts.map +1 -0
  37. package/dist/credentials/index.js +10 -0
  38. package/dist/credentials/index.js.map +1 -0
  39. package/dist/credentials/m2m.d.ts +40 -0
  40. package/dist/credentials/m2m.d.ts.map +1 -0
  41. package/dist/credentials/m2m.js +91 -0
  42. package/dist/credentials/m2m.js.map +1 -0
  43. package/dist/credentials/pat.d.ts +14 -0
  44. package/dist/credentials/pat.d.ts.map +1 -0
  45. package/dist/credentials/pat.js +41 -0
  46. package/dist/credentials/pat.js.map +1 -0
  47. package/dist/credentials/u2m.d.ts +31 -0
  48. package/dist/credentials/u2m.d.ts.map +1 -0
  49. package/dist/credentials/u2m.js +157 -0
  50. package/dist/credentials/u2m.js.map +1 -0
  51. package/dist/index.d.ts +10 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +10 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/oidc/env.d.ts +10 -0
  56. package/dist/oidc/env.d.ts.map +1 -0
  57. package/dist/oidc/env.js +19 -0
  58. package/dist/oidc/env.js.map +1 -0
  59. package/dist/oidc/file.d.ts +7 -0
  60. package/dist/oidc/file.d.ts.map +1 -0
  61. package/dist/oidc/file.js +28 -0
  62. package/dist/oidc/file.js.map +1 -0
  63. package/dist/oidc/index.browser.d.ts +13 -0
  64. package/dist/oidc/index.browser.d.ts.map +1 -0
  65. package/dist/oidc/index.browser.js +11 -0
  66. package/dist/oidc/index.browser.js.map +1 -0
  67. package/dist/oidc/index.d.ts +14 -0
  68. package/dist/oidc/index.d.ts.map +1 -0
  69. package/dist/oidc/index.js +12 -0
  70. package/dist/oidc/index.js.map +1 -0
  71. package/dist/oidc/oidc.d.ts +21 -0
  72. package/dist/oidc/oidc.d.ts.map +1 -0
  73. package/dist/oidc/oidc.js +10 -0
  74. package/dist/oidc/oidc.js.map +1 -0
  75. package/dist/oidc/tokensource.d.ts +56 -0
  76. package/dist/oidc/tokensource.d.ts.map +1 -0
  77. package/dist/oidc/tokensource.js +62 -0
  78. package/dist/oidc/tokensource.js.map +1 -0
  79. package/package.json +52 -4
  80. package/src/auth.ts +117 -0
  81. package/src/credentials/default/chain.ts +75 -0
  82. package/src/credentials/default/default-credentials.ts +40 -0
  83. package/src/credentials/default/errors.ts +18 -0
  84. package/src/credentials/default/u2m-strategy.ts +20 -0
  85. package/src/credentials/errors.ts +51 -0
  86. package/src/credentials/host-metadata.ts +166 -0
  87. package/src/credentials/index.browser.ts +11 -0
  88. package/src/credentials/index.ts +14 -0
  89. package/src/credentials/m2m.ts +156 -0
  90. package/src/credentials/pat.ts +44 -0
  91. package/src/credentials/u2m.ts +212 -0
  92. package/src/index.ts +19 -0
  93. package/src/oidc/env.ts +21 -0
  94. package/src/oidc/file.ts +29 -0
  95. package/src/oidc/index.browser.ts +16 -0
  96. package/src/oidc/index.ts +17 -0
  97. package/src/oidc/oidc.ts +26 -0
  98. package/src/oidc/tokensource.ts +133 -0
  99. package/index.js +0 -1
@@ -0,0 +1,40 @@
1
+ import type {Profile} from '@databricks/sdk-core/profiles';
2
+ import {resolve} from '@databricks/sdk-core/profiles';
3
+
4
+ import type {Credentials} from '../../auth';
5
+
6
+ import {DefaultCredentials, m2mStrategy, patStrategy} from './chain';
7
+ import type {Strategy} from './chain';
8
+ import {u2mStrategy} from './u2m-strategy';
9
+
10
+ const STRATEGIES: readonly Strategy[] = [patStrategy, m2mStrategy, u2mStrategy];
11
+
12
+ interface DefaultCredentialsOptions {
13
+ /**
14
+ * Pre-resolved profile. When omitted, the profile is resolved on first
15
+ * use from the default config file and environment variables.
16
+ */
17
+ profile?: Profile;
18
+ }
19
+
20
+ /**
21
+ * Returns a lazy {@link Credentials} that resolves to the first configured
22
+ * authentication strategy on first use.
23
+ *
24
+ * Strategies are tried in this order:
25
+ * 1. PAT (`pat`).
26
+ * 2. OAuth M2M (`oauth-m2m`).
27
+ * 3. Databricks CLI (`databricks-cli`).
28
+ *
29
+ * When no profile is provided via `options.profile`, the profile is
30
+ * resolved on first use from the default config file (~/.databrickscfg)
31
+ * and environment variables.
32
+ */
33
+ export function defaultCredentials(
34
+ options?: DefaultCredentialsOptions
35
+ ): Credentials {
36
+ const explicit = options?.profile;
37
+ const loadProfile = (): Promise<Profile> =>
38
+ explicit !== undefined ? Promise.resolve(explicit) : resolve();
39
+ return new DefaultCredentials(STRATEGIES, loadProfile);
40
+ }
@@ -0,0 +1,18 @@
1
+ /** Discriminant codes for {@link DefaultCredentialsError}. */
2
+ export type DefaultCredentialsErrorCode = 'NO_AUTH_CONFIGURED';
3
+
4
+ /**
5
+ * Error thrown when the default credentials chain cannot resolve a
6
+ * strategy to authenticate with.
7
+ *
8
+ * Use the `code` field to distinguish between error causes.
9
+ */
10
+ export class DefaultCredentialsError extends Error {
11
+ readonly code: DefaultCredentialsErrorCode;
12
+
13
+ constructor(code: DefaultCredentialsErrorCode, message: string) {
14
+ super(message);
15
+ this.name = 'DefaultCredentialsError';
16
+ this.code = code;
17
+ }
18
+ }
@@ -0,0 +1,20 @@
1
+ import {newU2mCredentials} from '../u2m';
2
+
3
+ import type {Strategy} from './chain';
4
+
5
+ /**
6
+ * U2M (Databricks CLI) strategy. Configured when the profile was loaded
7
+ * from the config file (so the section name is known) and a host is set.
8
+ * The CLI must have been logged in ahead of time via `databricks auth
9
+ * login`.
10
+ */
11
+ export const u2mStrategy: Strategy = profile => {
12
+ if (profile.host === undefined) return undefined;
13
+ if (profile.name === undefined) return undefined;
14
+ return newU2mCredentials({
15
+ profile: profile.name,
16
+ ...(profile.databricksCliPath !== undefined && {
17
+ cliPath: profile.databricksCliPath,
18
+ }),
19
+ });
20
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Error types for credential operations.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ /** Discriminant codes for {@link M2mCredentialsError}. */
8
+ export type M2mCredentialsErrorCode =
9
+ | 'CLIENT_ID_REQUIRED'
10
+ | 'CLIENT_SECRET_REQUIRED'
11
+ | 'HOST_REQUIRED'
12
+ | 'DISCOVERY_FAILED'
13
+ | 'TOKEN_REQUEST_FAILED';
14
+
15
+ /**
16
+ * Error thrown by M2M credential operations.
17
+ *
18
+ * Use the `code` field to distinguish between error causes.
19
+ */
20
+ export class M2mCredentialsError extends Error {
21
+ readonly code: M2mCredentialsErrorCode;
22
+
23
+ constructor(code: M2mCredentialsErrorCode, message: string) {
24
+ super(message);
25
+ this.name = 'M2mCredentialsError';
26
+ this.code = code;
27
+ }
28
+ }
29
+
30
+ /** Discriminant codes for {@link U2mCredentialsError}. */
31
+ export type U2mCredentialsErrorCode =
32
+ | 'PROFILE_REQUIRED'
33
+ | 'CLI_NOT_FOUND'
34
+ | 'LEGACY_CLI_DETECTED'
35
+ | 'TOKEN_FETCH_FAILED'
36
+ | 'INVALID_RESPONSE';
37
+
38
+ /**
39
+ * Error thrown by U2M (databricks-cli) credential operations.
40
+ *
41
+ * Use the `code` field to distinguish between error causes.
42
+ */
43
+ export class U2mCredentialsError extends Error {
44
+ readonly code: U2mCredentialsErrorCode;
45
+
46
+ constructor(code: U2mCredentialsErrorCode, message: string) {
47
+ super(message);
48
+ this.name = 'U2mCredentialsError';
49
+ this.code = code;
50
+ }
51
+ }
@@ -0,0 +1,166 @@
1
+ import {z} from 'zod';
2
+
3
+ /**
4
+ * HostMetadata holds the parsed response from the
5
+ * /.well-known/databricks-config discovery endpoint.
6
+ */
7
+ export interface HostMetadata {
8
+ /**
9
+ * oidcEndpoint is the OIDC discovery URL for this host. For account hosts,
10
+ * this may contain an {account_id} placeholder that callers must
11
+ * substitute.
12
+ */
13
+ oidcEndpoint: string;
14
+
15
+ /**
16
+ * accountId is the Databricks account ID associated with this host, if
17
+ * available.
18
+ */
19
+ accountId?: string;
20
+
21
+ /**
22
+ * workspaceId is the Databricks workspace ID associated with this host, if
23
+ * available.
24
+ */
25
+ workspaceId?: string;
26
+ }
27
+
28
+ /**
29
+ * getHostMetadata fetches the raw Databricks well-known configuration from
30
+ * {host}/.well-known/databricks-config. The returned HostMetadata contains
31
+ * raw values with no substitution (e.g., {account_id} placeholders are left
32
+ * as-is). Callers are responsible for interpreting the result.
33
+ */
34
+ export async function getHostMetadata(host: string): Promise<HostMetadata> {
35
+ const url = `${trimTrailingSlash(host)}/.well-known/databricks-config`;
36
+ let response: Response;
37
+ try {
38
+ response = await fetch(url);
39
+ } catch (e) {
40
+ throw new Error(
41
+ `fetching host metadata from ${url} failed: ${stringifyError(e)}`
42
+ );
43
+ }
44
+ if (!response.ok) {
45
+ const text = await safeReadText(response);
46
+ throw new Error(
47
+ `fetching host metadata from ${url} failed with status ` +
48
+ `${response.status.toString()}: ${text}`
49
+ );
50
+ }
51
+ let raw: unknown;
52
+ try {
53
+ raw = await response.json();
54
+ } catch (e) {
55
+ throw new Error(
56
+ `parsing host metadata from ${url} failed: ${stringifyError(e)}`
57
+ );
58
+ }
59
+ const parsed = hostMetadataSchema.parse(raw);
60
+ return {
61
+ oidcEndpoint: parsed.oidc_endpoint,
62
+ ...(parsed.account_id !== undefined && {accountId: parsed.account_id}),
63
+ ...(parsed.workspace_id !== undefined && {
64
+ workspaceId: parsed.workspace_id,
65
+ }),
66
+ };
67
+ }
68
+
69
+ /**
70
+ * resolveTokenEndpoint resolves the OAuth `token_endpoint` for the given
71
+ * Databricks host via a two-step discovery:
72
+ *
73
+ * 1. `GET {host}/.well-known/databricks-config` to obtain the OIDC root.
74
+ * 2. `GET {oidcRoot}/.well-known/oauth-authorization-server` to obtain the
75
+ * RFC 8414 authorization server metadata.
76
+ *
77
+ * If the OIDC root contains an `{account_id}` placeholder, it is substituted
78
+ * with the first defined value from: `configAccountId` (caller-supplied),
79
+ * then the `account_id` field returned by the host metadata response.
80
+ * Throws when any step fails, required fields are missing, or the placeholder
81
+ * cannot be resolved.
82
+ */
83
+ export async function resolveTokenEndpoint(
84
+ host: string,
85
+ configAccountId?: string
86
+ ): Promise<string> {
87
+ const meta = await getHostMetadata(host);
88
+ // Precedence mirrors the legacy SDK: a caller-supplied account ID
89
+ // overrides any value returned by the host metadata. The metadata
90
+ // back-fills only when the caller did not provide one.
91
+ const accountId =
92
+ configAccountId !== undefined && configAccountId !== ''
93
+ ? configAccountId
94
+ : meta.accountId;
95
+ let oidcRoot = meta.oidcEndpoint;
96
+ if (oidcRoot.includes('{account_id}')) {
97
+ if (accountId === undefined || accountId === '') {
98
+ throw new Error(
99
+ 'host metadata oidc_endpoint contains {account_id} placeholder but ' +
100
+ 'no account_id was provided or returned by the metadata response'
101
+ );
102
+ }
103
+ oidcRoot = oidcRoot.replaceAll('{account_id}', accountId);
104
+ }
105
+ const discoveryUrl = `${trimTrailingSlash(oidcRoot)}/.well-known/oauth-authorization-server`;
106
+ let response: Response;
107
+ try {
108
+ response = await fetch(discoveryUrl);
109
+ } catch (e) {
110
+ throw new Error(
111
+ `fetching oauth authorization server metadata from ${discoveryUrl} ` +
112
+ `failed: ${stringifyError(e)}`
113
+ );
114
+ }
115
+ if (!response.ok) {
116
+ const text = await safeReadText(response);
117
+ throw new Error(
118
+ `fetching oauth authorization server metadata from ${discoveryUrl} ` +
119
+ `failed with status ${response.status.toString()}: ${text}`
120
+ );
121
+ }
122
+ let raw: unknown;
123
+ try {
124
+ raw = await response.json();
125
+ } catch (e) {
126
+ throw new Error(
127
+ `parsing oauth authorization server metadata from ${discoveryUrl} ` +
128
+ `failed: ${stringifyError(e)}`
129
+ );
130
+ }
131
+ const parsed = oauthServerSchema.parse(raw);
132
+ return parsed.token_endpoint;
133
+ }
134
+
135
+ const hostMetadataSchema = z.object({
136
+ oidc_endpoint: z.string(),
137
+ account_id: z.string().optional(),
138
+ workspace_id: z.string().optional(),
139
+ });
140
+
141
+ const oauthServerSchema = z.object({
142
+ token_endpoint: z.string(),
143
+ });
144
+
145
+ function trimTrailingSlash(s: string): string {
146
+ let end = s.length;
147
+ while (end > 0 && s.charAt(end - 1) === '/') {
148
+ end--;
149
+ }
150
+ return s.slice(0, end);
151
+ }
152
+
153
+ async function safeReadText(response: Response): Promise<string> {
154
+ try {
155
+ return await response.text();
156
+ } catch {
157
+ return '';
158
+ }
159
+ }
160
+
161
+ function stringifyError(e: unknown): string {
162
+ if (e instanceof Error) {
163
+ return e.message;
164
+ }
165
+ return String(e);
166
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Browser entry point for credential implementations. Excludes credentials
3
+ * that depend on Node.js-only APIs (e.g. `databricks-cli` auth which spawns
4
+ * the CLI binary).
5
+ */
6
+
7
+ export {M2mCredentialsError} from './errors';
8
+ export type {M2mCredentialsErrorCode} from './errors';
9
+ export {newM2mCredentials} from './m2m';
10
+ export type {M2mCredentialsOptions} from './m2m';
11
+ export {newPatCredentials} from './pat';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Credential implementations for the Databricks SDK.
3
+ */
4
+
5
+ export {M2mCredentialsError, U2mCredentialsError} from './errors';
6
+ export type {M2mCredentialsErrorCode, U2mCredentialsErrorCode} from './errors';
7
+ export {newM2mCredentials} from './m2m';
8
+ export type {M2mCredentialsOptions} from './m2m';
9
+ export {newPatCredentials} from './pat';
10
+ export {newU2mCredentials} from './u2m';
11
+ export type {U2mCredentialsOptions} from './u2m';
12
+ export {defaultCredentials} from './default/default-credentials';
13
+ export {DefaultCredentialsError} from './default/errors';
14
+ export type {DefaultCredentialsErrorCode} from './default/errors';
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Machine-to-machine (M2M) OAuth credentials for the Databricks SDK.
3
+ */
4
+
5
+ import {z} from 'zod';
6
+
7
+ import type {Token, TokenCredentials} from '../auth';
8
+ import {newTokenCredentials, tokenProviderFn} from '../auth';
9
+
10
+ import {M2mCredentialsError} from './errors';
11
+ import {resolveTokenEndpoint} from './host-metadata';
12
+
13
+ /** Options for {@link newM2mCredentials}. */
14
+ export interface M2mCredentialsOptions {
15
+ /**
16
+ * Databricks host (workspace or account) used to discover the OAuth token
17
+ * endpoint.
18
+ */
19
+ host: string;
20
+
21
+ /**
22
+ * OAuth client ID issued to the service principal.
23
+ */
24
+ clientId: string;
25
+
26
+ /**
27
+ * OAuth client secret issued to the service principal.
28
+ */
29
+ clientSecret: string;
30
+
31
+ /**
32
+ * Databricks account ID. Required for account hosts.
33
+ */
34
+ accountId?: string;
35
+
36
+ /**
37
+ * OAuth scopes to request. When omitted or empty, defaults to
38
+ * `['all-apis']`.
39
+ */
40
+ scopes?: string[];
41
+ }
42
+
43
+ const DEFAULT_SCOPES: readonly string[] = ['all-apis'];
44
+
45
+ /**
46
+ * Creates a TokenCredentials that authenticates as a Databricks service
47
+ * principal using the OAuth client credentials grant.
48
+ *
49
+ * @param options - Host plus client credentials.
50
+ * @throws M2mCredentialsError when host, clientId, or clientSecret is empty,
51
+ * when token-endpoint discovery fails, or when the token endpoint returns
52
+ * an error.
53
+ */
54
+ export function newM2mCredentials(
55
+ options: M2mCredentialsOptions
56
+ ): TokenCredentials {
57
+ if (options.clientId === '') {
58
+ throw new M2mCredentialsError('CLIENT_ID_REQUIRED', 'clientId is required');
59
+ }
60
+ if (options.clientSecret === '') {
61
+ throw new M2mCredentialsError(
62
+ 'CLIENT_SECRET_REQUIRED',
63
+ 'clientSecret is required'
64
+ );
65
+ }
66
+ if (options.host === '') {
67
+ throw new M2mCredentialsError('HOST_REQUIRED', 'host is required');
68
+ }
69
+
70
+ const scopes =
71
+ options.scopes !== undefined && options.scopes.length > 0
72
+ ? options.scopes
73
+ : DEFAULT_SCOPES;
74
+
75
+ const body = new URLSearchParams({
76
+ grant_type: 'client_credentials',
77
+ scope: scopes.join(' '),
78
+ }).toString();
79
+
80
+ // Client ID and secret are URL-encoded before Basic auth encoding to
81
+ // avoid ambiguity with special characters in either value, matching the
82
+ // behavior of golang.org/x/oauth2.
83
+ const basicAuth = btoa(
84
+ `${encodeURIComponent(options.clientId)}:${encodeURIComponent(options.clientSecret)}`
85
+ );
86
+
87
+ let cachedTokenEndpoint: string | undefined;
88
+ const getTokenEndpoint = async (): Promise<string> => {
89
+ if (cachedTokenEndpoint === undefined) {
90
+ try {
91
+ cachedTokenEndpoint = await resolveTokenEndpoint(
92
+ options.host,
93
+ options.accountId
94
+ );
95
+ } catch (e) {
96
+ throw new M2mCredentialsError(
97
+ 'DISCOVERY_FAILED',
98
+ `discovering token endpoint failed: ${stringifyError(e)}`
99
+ );
100
+ }
101
+ }
102
+ return cachedTokenEndpoint;
103
+ };
104
+
105
+ const provider = tokenProviderFn(async () => {
106
+ const tokenEndpoint = await getTokenEndpoint();
107
+ return fetchAccessToken(tokenEndpoint, basicAuth, body);
108
+ });
109
+
110
+ return newTokenCredentials('oauth-m2m', provider);
111
+ }
112
+
113
+ async function fetchAccessToken(
114
+ tokenEndpoint: string,
115
+ basicAuth: string,
116
+ body: string
117
+ ): Promise<Token> {
118
+ const response = await fetch(tokenEndpoint, {
119
+ method: 'POST',
120
+ headers: {
121
+ Authorization: `Basic ${basicAuth}`,
122
+ 'Content-Type': 'application/x-www-form-urlencoded',
123
+ },
124
+ body,
125
+ });
126
+ if (!response.ok) {
127
+ const text = await response.text();
128
+ throw new M2mCredentialsError(
129
+ 'TOKEN_REQUEST_FAILED',
130
+ `token request failed with status ${response.status.toString()}: ${text}`
131
+ );
132
+ }
133
+ const parsed = tokenResponseSchema.parse(await response.json());
134
+ const expiry =
135
+ parsed.expires_in !== undefined
136
+ ? new Date(Date.now() + parsed.expires_in * 1000)
137
+ : undefined;
138
+ return {
139
+ value: parsed.access_token,
140
+ ...(parsed.token_type !== undefined && {type: parsed.token_type}),
141
+ ...(expiry !== undefined && {expiry}),
142
+ };
143
+ }
144
+
145
+ const tokenResponseSchema = z.object({
146
+ access_token: z.string(),
147
+ token_type: z.string().optional(),
148
+ expires_in: z.number().optional(),
149
+ });
150
+
151
+ function stringifyError(e: unknown): string {
152
+ if (e instanceof Error) {
153
+ return e.message;
154
+ }
155
+ return String(e);
156
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Personal Access Token (PAT) credentials for the Databricks SDK.
3
+ */
4
+
5
+ import type {Credentials, Header} from '../auth';
6
+
7
+ /**
8
+ * Error thrown when a token is required but not provided.
9
+ */
10
+ class TokenRequiredError extends Error {
11
+ constructor() {
12
+ super('token is required');
13
+ this.name = 'TokenRequiredError';
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Creates a Credentials that can be used to authenticate with a Personal
19
+ * Access Token.
20
+ *
21
+ * @param token - The personal access token.
22
+ * @returns Credentials for PAT authentication.
23
+ * @throws TokenRequiredError if token is empty.
24
+ */
25
+ export function newPatCredentials(token: string): Credentials {
26
+ if (token === '') {
27
+ throw new TokenRequiredError();
28
+ }
29
+ return new PatCredentials(token);
30
+ }
31
+
32
+ class PatCredentials implements Credentials {
33
+ constructor(private readonly token: string) {}
34
+
35
+ name(): string {
36
+ return 'pat';
37
+ }
38
+
39
+ authHeaders(): Promise<Header[]> {
40
+ return Promise.resolve([
41
+ {key: 'Authorization', value: `Bearer ${this.token}`},
42
+ ]);
43
+ }
44
+ }