@palantir/pack.auth.foundry 0.0.1-beta.1

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 (48) hide show
  1. package/.turbo/turbo-lint.log +20 -0
  2. package/.turbo/turbo-transpileBrowser.log +5 -0
  3. package/.turbo/turbo-transpileCjs.log +5 -0
  4. package/.turbo/turbo-transpileEsm.log +5 -0
  5. package/.turbo/turbo-transpileTypes.log +5 -0
  6. package/.turbo/turbo-typecheck.log +4 -0
  7. package/LICENSE.txt +13 -0
  8. package/README.md +159 -0
  9. package/build/browser/index.js +374 -0
  10. package/build/browser/index.js.map +1 -0
  11. package/build/cjs/index.cjs +381 -0
  12. package/build/cjs/index.cjs.map +1 -0
  13. package/build/cjs/index.d.cts +90 -0
  14. package/build/esm/index.js +374 -0
  15. package/build/esm/index.js.map +1 -0
  16. package/build/types/AuthModuleConfig.d.ts +1 -0
  17. package/build/types/AuthModuleConfig.d.ts.map +1 -0
  18. package/build/types/__tests__/AuthServiceConfidential.test.d.ts +1 -0
  19. package/build/types/__tests__/AuthServiceConfidential.test.d.ts.map +1 -0
  20. package/build/types/__tests__/AuthServicePublic.test.d.ts +1 -0
  21. package/build/types/__tests__/AuthServicePublic.test.d.ts.map +1 -0
  22. package/build/types/__tests__/AuthServiceStatic.test.d.ts +1 -0
  23. package/build/types/__tests__/AuthServiceStatic.test.d.ts.map +1 -0
  24. package/build/types/auth/AuthServiceBase.d.ts +32 -0
  25. package/build/types/auth/AuthServiceBase.d.ts.map +1 -0
  26. package/build/types/auth/AuthServiceConfidential.d.ts +20 -0
  27. package/build/types/auth/AuthServiceConfidential.d.ts.map +1 -0
  28. package/build/types/auth/AuthServicePublic.d.ts +23 -0
  29. package/build/types/auth/AuthServicePublic.d.ts.map +1 -0
  30. package/build/types/auth/AuthServiceStatic.d.ts +17 -0
  31. package/build/types/auth/AuthServiceStatic.d.ts.map +1 -0
  32. package/build/types/index.d.ts +4 -0
  33. package/build/types/index.d.ts.map +1 -0
  34. package/build/types/utils/jwtUtils.d.ts +28 -0
  35. package/build/types/utils/jwtUtils.d.ts.map +1 -0
  36. package/package.json +74 -0
  37. package/src/AuthModuleConfig.ts +17 -0
  38. package/src/__tests__/AuthServiceConfidential.test.ts +407 -0
  39. package/src/__tests__/AuthServicePublic.test.ts +413 -0
  40. package/src/__tests__/AuthServiceStatic.test.ts +341 -0
  41. package/src/auth/AuthServiceBase.ts +201 -0
  42. package/src/auth/AuthServiceConfidential.ts +96 -0
  43. package/src/auth/AuthServicePublic.ts +124 -0
  44. package/src/auth/AuthServiceStatic.ts +91 -0
  45. package/src/index.ts +20 -0
  46. package/src/utils/jwtUtils.ts +127 -0
  47. package/tsconfig.json +21 -0
  48. package/vitest.config.mjs +26 -0
@@ -0,0 +1,124 @@
1
+ /*
2
+ * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import type { PublicOauthClient } from "@osdk/oauth";
18
+ import { AuthState, type AuthStateCallback } from "@palantir/pack.auth";
19
+ import type { PackAppInternal, Unsubscribe } from "@palantir/pack.core";
20
+ import { AuthServiceBase } from "./AuthServiceBase.js";
21
+
22
+ export class AuthServicePublic extends AuthServiceBase {
23
+ private hasInitialized = false;
24
+ private readonly oauthClient: PublicOauthClient;
25
+
26
+ constructor(app: PackAppInternal, oauthClient: PublicOauthClient) {
27
+ super(
28
+ app,
29
+ app.config.logger.child({}, { msgPrefix: "AuthServicePublic" }),
30
+ AuthState.Initializing,
31
+ );
32
+ this.oauthClient = oauthClient;
33
+
34
+ this.oauthClient.addEventListener("signIn", this.signInHandler);
35
+ this.oauthClient.addEventListener("signOut", this.signOutHandler);
36
+ this.oauthClient.addEventListener("refresh", this.refreshHandler);
37
+ }
38
+
39
+ override async getToken(): Promise<string> {
40
+ return this.oauthClient();
41
+ }
42
+
43
+ private readonly signInHandler = () => {
44
+ const token = this.getTokenOrUndefined();
45
+ this.notifyTokenChange(token);
46
+ this.updateState(AuthState.Authenticated);
47
+ };
48
+
49
+ private readonly signOutHandler = () => {
50
+ this.notifyTokenChange(undefined);
51
+ this.updateState(AuthState.Unauthenticated);
52
+ };
53
+
54
+ private readonly refreshHandler = () => {
55
+ const token = this.getTokenOrUndefined();
56
+ this.notifyTokenChange(token);
57
+ this.updateState(AuthState.Authenticated);
58
+ };
59
+
60
+ override getTokenOrUndefined(): string | undefined {
61
+ return this.oauthClient.getTokenOrUndefined();
62
+ }
63
+
64
+ override onAuthStateChange(callback: AuthStateCallback): Unsubscribe {
65
+ const unsubscribe = super.onAuthStateChange(callback);
66
+
67
+ if (!this.hasInitialized && this.currentState === AuthState.Initializing) {
68
+ this.hasInitialized = true;
69
+ void this.signIn().catch(() => {
70
+ // Don't allow unhandled rejection to bubble up, mostly for testing.
71
+ // Note the signIn method will log errors and throw for promise consumers,
72
+ // but we don't need to handle anything here.
73
+ });
74
+ }
75
+
76
+ return unsubscribe;
77
+ }
78
+
79
+ override async signIn(): Promise<void> {
80
+ try {
81
+ this.updateState(AuthState.Authenticating);
82
+ await this.oauthClient.signIn();
83
+ } catch (error) {
84
+ this.updateState(AuthState.Error, error instanceof Error ? error : new Error(String(error)));
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ override async signOut(): Promise<void> {
90
+ try {
91
+ this.clearUserState();
92
+ await this.oauthClient.signOut();
93
+ } catch (error) {
94
+ this.updateState(AuthState.Error, error instanceof Error ? error : new Error(String(error)));
95
+ throw error;
96
+ }
97
+ }
98
+
99
+ override async refresh(): Promise<void> {
100
+ try {
101
+ await this.oauthClient.refresh();
102
+ } catch (error) {
103
+ this.updateState(AuthState.Error, error instanceof Error ? error : new Error(String(error)));
104
+ throw error;
105
+ }
106
+ }
107
+
108
+ override dispose(): void {
109
+ this.oauthClient.removeEventListener("signIn", this.signInHandler);
110
+ this.oauthClient.removeEventListener("signOut", this.signOutHandler);
111
+ this.oauthClient.removeEventListener("refresh", this.refreshHandler);
112
+ super.dispose();
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Creates a public OAuth auth service that wraps a PublicOauthClient
118
+ */
119
+ export function createPublicOauthService(
120
+ app: PackAppInternal,
121
+ oauthClient: PublicOauthClient,
122
+ ): AuthServicePublic {
123
+ return new AuthServicePublic(app, oauthClient);
124
+ }
@@ -0,0 +1,91 @@
1
+ /*
2
+ * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { AuthState } from "@palantir/pack.auth";
18
+ import type { PackAppInternal, TokenProvider } from "@palantir/pack.core";
19
+ import { AuthServiceBase } from "./AuthServiceBase.js";
20
+
21
+ export class AuthServiceStatic extends AuthServiceBase {
22
+ private readonly tokenProvider: TokenProvider;
23
+ private latestToken: string | undefined;
24
+
25
+ constructor(app: PackAppInternal, tokenProvider: TokenProvider) {
26
+ super(
27
+ app,
28
+ app.config.logger.child({}, { msgPrefix: "AuthServiceStatic" }),
29
+ AuthState.Unauthenticated,
30
+ );
31
+ this.tokenProvider = tokenProvider;
32
+ void this.getToken().catch(() => {});
33
+ }
34
+
35
+ override async getToken(): Promise<string> {
36
+ try {
37
+ const token = await this.tokenProvider();
38
+
39
+ if (this.latestToken !== token) {
40
+ this.latestToken = token;
41
+ this.notifyTokenChange(token);
42
+ if (this.currentState !== AuthState.Authenticated) {
43
+ this.updateState(AuthState.Authenticated);
44
+ }
45
+ }
46
+
47
+ return token;
48
+ } catch (error) {
49
+ if (this.latestToken !== undefined) {
50
+ this.latestToken = undefined;
51
+ this.notifyTokenChange(undefined);
52
+ }
53
+ this.updateState(AuthState.Error, error as Error);
54
+ throw error;
55
+ }
56
+ }
57
+
58
+ override getTokenOrUndefined(): string | undefined {
59
+ return this.latestToken;
60
+ }
61
+
62
+ override isAuthenticated(): boolean {
63
+ return this.latestToken != null;
64
+ }
65
+
66
+ override signIn(): Promise<void> {
67
+ return Promise.resolve();
68
+ }
69
+
70
+ override signOut(): Promise<void> {
71
+ this.clearUserState();
72
+ this.latestToken = undefined;
73
+ this.updateState(AuthState.Unauthenticated);
74
+ return Promise.reject(new Error("Sign out not supported for static token providers"));
75
+ }
76
+
77
+ override dispose(): void {
78
+ super.dispose();
79
+ this.latestToken = undefined;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Creates a static token auth service for CLI/service scenarios where a token is provided externally.
85
+ */
86
+ export function createStaticTokenService(
87
+ app: PackAppInternal,
88
+ tokenProvider: TokenProvider,
89
+ ): AuthServiceStatic {
90
+ return new AuthServiceStatic(app, tokenProvider);
91
+ }
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ export { createConfidentialOauthService } from "./auth/AuthServiceConfidential.js";
18
+ export { createPublicOauthService } from "./auth/AuthServicePublic.js";
19
+ export { createStaticTokenService } from "./auth/AuthServiceStatic.js";
20
+ export { createAuthModuleConfig } from "./AuthModuleConfig.js";
@@ -0,0 +1,127 @@
1
+ /*
2
+ * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import type { UserId } from "@palantir/pack.auth";
18
+
19
+ /**
20
+ * Decode a Palantir-specific JWT claim that stores UUIDs as 16-byte arrays.
21
+ * These are the sub, sid, jti, and org claims.
22
+ */
23
+ function maybeDecodeUuidClaim(value: unknown): string | undefined {
24
+ if (!Array.isArray(value)) {
25
+ return undefined;
26
+ }
27
+
28
+ if (value.length === 16 && value.every(v => typeof v === "number")) {
29
+ // Convert 16-byte array to UUID string
30
+ return decodeUuidBytes(value);
31
+ }
32
+
33
+ return undefined;
34
+ }
35
+
36
+ /**
37
+ * Convert a 16-byte array to UUID string format.
38
+ * Based on Palantir's UUID encoding format.
39
+ */
40
+ function decodeUuidBytes(bytes: readonly number[]): string {
41
+ if (bytes.length !== 16) {
42
+ return "";
43
+ }
44
+
45
+ // Convert bytes to hex string
46
+ const hex = bytes.map(b => b.toString(16).padStart(2, "0")).join("");
47
+
48
+ // Format as UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
49
+ return [
50
+ hex.slice(0, 8),
51
+ hex.slice(8, 12),
52
+ hex.slice(12, 16),
53
+ hex.slice(16, 20),
54
+ hex.slice(20, 32),
55
+ ].join("-");
56
+ }
57
+
58
+ /**
59
+ * Represents the parsed payload from a JWT token (unverified).
60
+ * The information provided should not be used for security-sensitive
61
+ * applications unless verified through some other process.
62
+ */
63
+ export interface UnverifiedTokenInfo {
64
+ readonly expiryInstantUtc?: number;
65
+ readonly orgId?: string;
66
+ readonly sessionId?: string;
67
+ readonly tokenId?: string;
68
+ readonly userId?: UserId;
69
+ }
70
+
71
+ /**
72
+ * Parse JWT token to extract unverified claims including userId, sessionId, tokenId, organizationId, and expiry.
73
+ * This does NOT verify the token signature - it only extracts payload data.
74
+ *
75
+ * @param token - JWT token string
76
+ * @returns Unverified token info or undefined if parsing fails
77
+ */
78
+ export function parseJwtToken(token: string): UnverifiedTokenInfo | undefined {
79
+ if (!token) {
80
+ return undefined;
81
+ }
82
+
83
+ try {
84
+ // JWT tokens have 3 parts separated by dots: header.payload.signature
85
+ const parts = token.split(".");
86
+ if (parts.length !== 3) {
87
+ return undefined;
88
+ }
89
+
90
+ // Decode the payload (second part)
91
+ const payload = parts[1];
92
+ if (!payload) {
93
+ return undefined;
94
+ }
95
+
96
+ // Add padding if needed for base64 decoding
97
+ const paddedPayload = payload.padEnd(payload.length + (4 - payload.length % 4) % 4, "=");
98
+
99
+ const decoded = JSON.parse(atob(paddedPayload)) as Record<string, unknown>;
100
+
101
+ return {
102
+ // JWT exp is seconds since epoch, convert to ms
103
+ expiryInstantUtc: typeof decoded.exp === "number" ? decoded.exp * 1000 : undefined,
104
+ orgId: maybeDecodeUuidClaim(decoded.org),
105
+ sessionId: maybeDecodeUuidClaim(decoded.sid),
106
+ tokenId: maybeDecodeUuidClaim(decoded.jti),
107
+ userId: maybeDecodeUuidClaim(decoded.sub),
108
+ };
109
+ } catch {
110
+ return undefined;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Check if a JWT token is expired based on its exp claim (unverified).
116
+ *
117
+ * @param tokenInfo - Unverified token info
118
+ * @returns true if token is expired, false if valid or no expiry info
119
+ */
120
+ export function isTokenExpired(tokenInfo: UnverifiedTokenInfo): boolean {
121
+ if (!tokenInfo.expiryInstantUtc) {
122
+ return false; // No expiry info, assume valid
123
+ }
124
+
125
+ const now = Date.now();
126
+ return tokenInfo.expiryInstantUtc < now;
127
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "@palantir/pack.monorepo.tsconfig/base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./build/esm",
5
+ "rootDir": "./src",
6
+ "declarationDir": "./build/types",
7
+ "tsBuildInfoFile": "./build/tsconfig.tsbuildinfo"
8
+ },
9
+ "include": [
10
+ "src/**/*"
11
+ ],
12
+ "exclude": [
13
+ "build",
14
+ "dist",
15
+ "node_modules",
16
+ "test-output",
17
+ "**/__tests__/**/__snapshots__",
18
+ "**/__tests__/**/fixtures"
19
+ ],
20
+ "references": []
21
+ }
@@ -0,0 +1,26 @@
1
+ import { coverageConfigDefaults, defaultExclude, defineProject } from "vitest/config";
2
+
3
+ export default defineProject({
4
+ test: {
5
+ exclude: [...defaultExclude, "**/build/**", "**/test-output/**"],
6
+ coverage: {
7
+ provider: "v8",
8
+ all: true,
9
+ enabled: true,
10
+ pool: "forks",
11
+ include: ["src/**/*.{ts,tsx}"],
12
+ exclude: [
13
+ ...coverageConfigDefaults.exclude,
14
+ "**/*.test.{ts,tsx}",
15
+ "**/__tests__",
16
+ "build",
17
+ "coverage",
18
+ "dist",
19
+ "node_modules",
20
+ "src/index.ts",
21
+ "test-output",
22
+ "vitest.config.*",
23
+ ],
24
+ },
25
+ },
26
+ });