unotoken 0.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 +360 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1207 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +15 -0
- package/dist/client.js.map +1 -0
- package/dist/db.d.ts +52 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +97 -0
- package/dist/db.js.map +1 -0
- package/dist/dotenv.d.ts +69 -0
- package/dist/dotenv.d.ts.map +1 -0
- package/dist/dotenv.js +115 -0
- package/dist/dotenv.js.map +1 -0
- package/dist/env-mapper.d.ts +55 -0
- package/dist/env-mapper.d.ts.map +1 -0
- package/dist/env-mapper.js +97 -0
- package/dist/env-mapper.js.map +1 -0
- package/dist/exec.d.ts +80 -0
- package/dist/exec.d.ts.map +1 -0
- package/dist/exec.js +214 -0
- package/dist/exec.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth/commands.d.ts +151 -0
- package/dist/oauth/commands.d.ts.map +1 -0
- package/dist/oauth/commands.js +322 -0
- package/dist/oauth/commands.js.map +1 -0
- package/dist/oauth/config.d.ts +84 -0
- package/dist/oauth/config.d.ts.map +1 -0
- package/dist/oauth/config.js +156 -0
- package/dist/oauth/config.js.map +1 -0
- package/dist/oauth/crypto-helpers.d.ts +44 -0
- package/dist/oauth/crypto-helpers.d.ts.map +1 -0
- package/dist/oauth/crypto-helpers.js +94 -0
- package/dist/oauth/crypto-helpers.js.map +1 -0
- package/dist/oauth/device-secret.d.ts +57 -0
- package/dist/oauth/device-secret.d.ts.map +1 -0
- package/dist/oauth/device-secret.js +106 -0
- package/dist/oauth/device-secret.js.map +1 -0
- package/dist/oauth/flow.d.ts +112 -0
- package/dist/oauth/flow.d.ts.map +1 -0
- package/dist/oauth/flow.js +255 -0
- package/dist/oauth/flow.js.map +1 -0
- package/dist/oauth/index.d.ts +18 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +24 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/oauth/key-wrap.d.ts +146 -0
- package/dist/oauth/key-wrap.d.ts.map +1 -0
- package/dist/oauth/key-wrap.js +275 -0
- package/dist/oauth/key-wrap.js.map +1 -0
- package/dist/oauth/pkce.d.ts +29 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/pkce.js +34 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/oauth/provider.d.ts +79 -0
- package/dist/oauth/provider.d.ts.map +1 -0
- package/dist/oauth/provider.js +10 -0
- package/dist/oauth/provider.js.map +1 -0
- package/dist/oauth/providers/github.d.ts +75 -0
- package/dist/oauth/providers/github.d.ts.map +1 -0
- package/dist/oauth/providers/github.js +119 -0
- package/dist/oauth/providers/github.js.map +1 -0
- package/dist/oauth/providers/google.d.ts +115 -0
- package/dist/oauth/providers/google.d.ts.map +1 -0
- package/dist/oauth/providers/google.js +285 -0
- package/dist/oauth/providers/google.js.map +1 -0
- package/dist/sdk.d.ts +8 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +8 -0
- package/dist/sdk.js.map +1 -0
- package/dist/server.d.ts +33 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +287 -0
- package/dist/server.js.map +1 -0
- package/dist/signatures/approval-codes.d.ts +192 -0
- package/dist/signatures/approval-codes.d.ts.map +1 -0
- package/dist/signatures/approval-codes.js +407 -0
- package/dist/signatures/approval-codes.js.map +1 -0
- package/dist/signatures/commands.d.ts +108 -0
- package/dist/signatures/commands.d.ts.map +1 -0
- package/dist/signatures/commands.js +270 -0
- package/dist/signatures/commands.js.map +1 -0
- package/dist/signatures/devices.d.ts +165 -0
- package/dist/signatures/devices.d.ts.map +1 -0
- package/dist/signatures/devices.js +344 -0
- package/dist/signatures/devices.js.map +1 -0
- package/dist/signatures/email-config.d.ts +102 -0
- package/dist/signatures/email-config.d.ts.map +1 -0
- package/dist/signatures/email-config.js +188 -0
- package/dist/signatures/email-config.js.map +1 -0
- package/dist/signatures/email.d.ts +106 -0
- package/dist/signatures/email.d.ts.map +1 -0
- package/dist/signatures/email.js +180 -0
- package/dist/signatures/email.js.map +1 -0
- package/dist/signatures/fingerprint.d.ts +70 -0
- package/dist/signatures/fingerprint.d.ts.map +1 -0
- package/dist/signatures/fingerprint.js +123 -0
- package/dist/signatures/fingerprint.js.map +1 -0
- package/dist/signatures/guard.d.ts +118 -0
- package/dist/signatures/guard.d.ts.map +1 -0
- package/dist/signatures/guard.js +310 -0
- package/dist/signatures/guard.js.map +1 -0
- package/dist/signatures/resend.d.ts +84 -0
- package/dist/signatures/resend.d.ts.map +1 -0
- package/dist/signatures/resend.js +248 -0
- package/dist/signatures/resend.js.map +1 -0
- package/dist/token-requests.d.ts +80 -0
- package/dist/token-requests.d.ts.map +1 -0
- package/dist/token-requests.js +201 -0
- package/dist/token-requests.js.map +1 -0
- package/dist/tokens.d.ts +80 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +150 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth module for unotoken.
|
|
3
|
+
*
|
|
4
|
+
* Provides a provider-agnostic PKCE browser flow engine
|
|
5
|
+
* for authenticating users via OAuth/OIDC providers,
|
|
6
|
+
* plus key wrapping for OAuth-derived vault unlock.
|
|
7
|
+
*/
|
|
8
|
+
export { generatePKCE, computeChallenge, type PKCEPair } from './pkce.js';
|
|
9
|
+
export type { OAuthProvider, OAuthIdentity, TokenResponse, } from './provider.js';
|
|
10
|
+
export { runOAuthFlow, startCallbackServer, openSystemBrowser, exchangeCode, OAuthTimeoutError, OAuthCallbackError, OAuthTokenError, type OAuthFlowConfig, type OAuthFlowResult, } from './flow.js';
|
|
11
|
+
export { wrapMasterKey, unwrapMasterKey, listOAuthLinks, removeOAuthLink, initOAuthLinksTable, deriveWrapKey, encryptMasterKey, decryptMasterKey, KeyUnwrapError, WRAP_KEY_BYTES, WRAP_SALT_BYTES, WRAP_NONCE_BYTES, type OAuthLinkRow, type OAuthLinksDb, } from './key-wrap.js';
|
|
12
|
+
export { generateDeviceSecret, readDeviceSecret, deviceSecretExists, getDeviceKeyPath, getDeviceDir, DeviceSecretMissingError, DEVICE_SECRET_BYTES, } from './device-secret.js';
|
|
13
|
+
export { createGoogleProvider, clearGoogleCaches, type GoogleProviderOptions, type GoogleDiscoveryDocument, type GoogleIdTokenClaims, type JWK, type JWKSResponse, } from './providers/google.js';
|
|
14
|
+
export { createGitHubProvider, type GitHubProviderOptions, type GitHubUser, } from './providers/github.js';
|
|
15
|
+
export { deriveMasterKey, decryptWithKey, verifyMasterKey, SALT_BYTES, KEY_BYTES, NONCE_BYTES, } from './crypto-helpers.js';
|
|
16
|
+
export { authLink, unlockWithOAuth, listLinkedProviders, unlinkProvider, isValidProvider, resolveProvider, MultipleProvidersError, type ProviderName, type AuthLinkOptions, type AuthLinkResult, type UnlockOAuthOptions, type UnlockOAuthResult, type UnlinkResult, type LinkedProvider, } from './commands.js';
|
|
17
|
+
export { loadOAuthConfig, saveOAuthConfig, resetOAuthConfig, updateOAuthConfig, hasCustomConfig, formatConfigForDisplay, maskSecret, getConfigPath, type OAuthConfigFile, } from './config.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1E,YAAY,EACV,aAAa,EACb,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,GAAG,EACR,KAAK,YAAY,GAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,UAAU,GAChB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,eAAe,EACf,cAAc,EACd,eAAe,EACf,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,UAAU,EACV,aAAa,EACb,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth module for unotoken.
|
|
3
|
+
*
|
|
4
|
+
* Provides a provider-agnostic PKCE browser flow engine
|
|
5
|
+
* for authenticating users via OAuth/OIDC providers,
|
|
6
|
+
* plus key wrapping for OAuth-derived vault unlock.
|
|
7
|
+
*/
|
|
8
|
+
export { generatePKCE, computeChallenge } from './pkce.js';
|
|
9
|
+
export { runOAuthFlow, startCallbackServer, openSystemBrowser, exchangeCode, OAuthTimeoutError, OAuthCallbackError, OAuthTokenError, } from './flow.js';
|
|
10
|
+
// Key wrapping for OAuth-derived unlock (US-003)
|
|
11
|
+
export { wrapMasterKey, unwrapMasterKey, listOAuthLinks, removeOAuthLink, initOAuthLinksTable, deriveWrapKey, encryptMasterKey, decryptMasterKey, KeyUnwrapError, WRAP_KEY_BYTES, WRAP_SALT_BYTES, WRAP_NONCE_BYTES, } from './key-wrap.js';
|
|
12
|
+
// Device secret management (US-003)
|
|
13
|
+
export { generateDeviceSecret, readDeviceSecret, deviceSecretExists, getDeviceKeyPath, getDeviceDir, DeviceSecretMissingError, DEVICE_SECRET_BYTES, } from './device-secret.js';
|
|
14
|
+
// Google OIDC provider (US-004)
|
|
15
|
+
export { createGoogleProvider, clearGoogleCaches, } from './providers/google.js';
|
|
16
|
+
// GitHub OAuth provider (US-005)
|
|
17
|
+
export { createGitHubProvider, } from './providers/github.js';
|
|
18
|
+
// Crypto helpers for vault operations (US-006)
|
|
19
|
+
export { deriveMasterKey, decryptWithKey, verifyMasterKey, SALT_BYTES, KEY_BYTES, NONCE_BYTES, } from './crypto-helpers.js';
|
|
20
|
+
// OAuth CLI commands (US-006 + US-007)
|
|
21
|
+
export { authLink, unlockWithOAuth, listLinkedProviders, unlinkProvider, isValidProvider, resolveProvider, MultipleProvidersError, } from './commands.js';
|
|
22
|
+
// OAuth config management (US-007)
|
|
23
|
+
export { loadOAuthConfig, saveOAuthConfig, resetOAuthConfig, updateOAuthConfig, hasCustomConfig, formatConfigForDisplay, maskSecret, getConfigPath, } from './config.js';
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAiB,MAAM,WAAW,CAAC;AAQ1E,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAGhB,MAAM,WAAW,CAAC;AAEnB,iDAAiD;AACjD,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,GAGjB,MAAM,eAAe,CAAC;AAEvB,oCAAoC;AACpC,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,gCAAgC;AAChC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,GAMlB,MAAM,uBAAuB,CAAC;AAE/B,iCAAiC;AACjC,OAAO,EACL,oBAAoB,GAGrB,MAAM,uBAAuB,CAAC;AAE/B,+CAA+C;AAC/C,OAAO,EACL,eAAe,EACf,cAAc,EACd,eAAe,EACf,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAE7B,uCAAuC;AACvC,OAAO,EACL,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,eAAe,EACf,sBAAsB,GAQvB,MAAM,eAAe,CAAC;AAEvB,mCAAmC;AACnC,OAAO,EACL,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,UAAU,EACV,aAAa,GAEd,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth key wrapping — encrypt/decrypt the vault master key using an
|
|
3
|
+
* OAuth-derived wrap key.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. User authenticates via OAuth (gets provider + subject_id)
|
|
7
|
+
* 2. Wrap key = HKDF-SHA256(ikm=device_secret, salt=random, info=provider+subject_id)
|
|
8
|
+
* 3. Wrapped key = XChaCha20-Poly1305(plaintext=master_key, key=wrap_key)
|
|
9
|
+
* 4. Stored in SQLite oauth_links table alongside salt and nonce
|
|
10
|
+
*
|
|
11
|
+
* Unwrap flow:
|
|
12
|
+
* 1. User authenticates via OAuth (gets provider + subject_id)
|
|
13
|
+
* 2. Look up row in oauth_links
|
|
14
|
+
* 3. Re-derive wrap key from device_secret + stored salt
|
|
15
|
+
* 4. Decrypt wrapped_key → master_key
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
/** Key/nonce/salt sizes */
|
|
20
|
+
export declare const WRAP_KEY_BYTES = 32;
|
|
21
|
+
export declare const WRAP_SALT_BYTES = 32;
|
|
22
|
+
export declare const WRAP_NONCE_BYTES = 24;
|
|
23
|
+
/**
|
|
24
|
+
* Row shape for the oauth_links SQLite table.
|
|
25
|
+
*/
|
|
26
|
+
export interface OAuthLinkRow {
|
|
27
|
+
provider: string;
|
|
28
|
+
subject_id: string;
|
|
29
|
+
wrap_salt: Buffer;
|
|
30
|
+
wrapped_key: Buffer;
|
|
31
|
+
nonce: Buffer;
|
|
32
|
+
created_at: string;
|
|
33
|
+
last_used_at: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Minimal database interface for oauth_links table operations.
|
|
37
|
+
*
|
|
38
|
+
* This abstraction allows the key-wrap module to work with any sql.js
|
|
39
|
+
* database instance (typically obtained via VaultEngine.getDb().getRawDb()).
|
|
40
|
+
*
|
|
41
|
+
* sql.js Database objects expose: run(), exec(), prepare().
|
|
42
|
+
* Parameterized queries use prepare() → bind() → step() → getAsObject() → free().
|
|
43
|
+
*/
|
|
44
|
+
export interface OAuthLinksDb {
|
|
45
|
+
run(sql: string, params?: unknown[]): void;
|
|
46
|
+
exec(sql: string): void;
|
|
47
|
+
prepare(sql: string): {
|
|
48
|
+
bind(params?: unknown[]): boolean;
|
|
49
|
+
step(): boolean;
|
|
50
|
+
getAsObject(): Record<string, unknown>;
|
|
51
|
+
free(): void;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Initialize the oauth_links table if it does not exist.
|
|
56
|
+
*
|
|
57
|
+
* Called once when the key-wrap module is first used against a database.
|
|
58
|
+
*/
|
|
59
|
+
export declare function initOAuthLinksTable(db: OAuthLinksDb): void;
|
|
60
|
+
/**
|
|
61
|
+
* Derive a 32-byte wrap key using HKDF-SHA256.
|
|
62
|
+
*
|
|
63
|
+
* @param deviceSecret - 32-byte device secret (IKM)
|
|
64
|
+
* @param wrapSalt - 32-byte random salt (unique per link)
|
|
65
|
+
* @param provider - OAuth provider name (e.g., 'google')
|
|
66
|
+
* @param subjectId - Provider-specific user ID
|
|
67
|
+
* @returns 32-byte wrap key
|
|
68
|
+
*/
|
|
69
|
+
export declare function deriveWrapKey(deviceSecret: Buffer, wrapSalt: Buffer, provider: string, subjectId: string): Buffer;
|
|
70
|
+
/**
|
|
71
|
+
* Wrap (encrypt) the master key with an OAuth-derived wrap key.
|
|
72
|
+
*
|
|
73
|
+
* Uses XChaCha20-Poly1305 for authenticated encryption.
|
|
74
|
+
*
|
|
75
|
+
* @param masterKey - The vault master key to wrap (32 bytes)
|
|
76
|
+
* @param wrapKey - The HKDF-derived wrap key (32 bytes)
|
|
77
|
+
* @returns Object containing wrapped (encrypted) key and the nonce used
|
|
78
|
+
*/
|
|
79
|
+
export declare function encryptMasterKey(masterKey: Buffer, wrapKey: Buffer): Promise<{
|
|
80
|
+
wrappedKey: Buffer;
|
|
81
|
+
nonce: Buffer;
|
|
82
|
+
}>;
|
|
83
|
+
/**
|
|
84
|
+
* Unwrap (decrypt) the master key with an OAuth-derived wrap key.
|
|
85
|
+
*
|
|
86
|
+
* Uses XChaCha20-Poly1305 for authenticated decryption.
|
|
87
|
+
*
|
|
88
|
+
* @param wrappedKey - The encrypted master key
|
|
89
|
+
* @param nonce - The nonce used during encryption
|
|
90
|
+
* @param wrapKey - The HKDF-derived wrap key (32 bytes)
|
|
91
|
+
* @returns The decrypted master key (32 bytes)
|
|
92
|
+
* @throws Error if the wrap key is wrong or data is corrupted
|
|
93
|
+
*/
|
|
94
|
+
export declare function decryptMasterKey(wrappedKey: Buffer, nonce: Buffer, wrapKey: Buffer): Promise<Buffer>;
|
|
95
|
+
/**
|
|
96
|
+
* Error thrown when key unwrapping fails.
|
|
97
|
+
*/
|
|
98
|
+
export declare class KeyUnwrapError extends Error {
|
|
99
|
+
constructor(message: string);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Wrap the master key for an OAuth identity and store in the database.
|
|
103
|
+
*
|
|
104
|
+
* This is the main entry point for linking an OAuth identity.
|
|
105
|
+
* Called after the user authenticates via OAuth while the vault is unlocked.
|
|
106
|
+
*
|
|
107
|
+
* @param db - sql.js database instance (from VaultEngine.getDb().getRawDb())
|
|
108
|
+
* @param masterKey - The current vault master key (32 bytes)
|
|
109
|
+
* @param provider - OAuth provider name (e.g., 'google', 'github')
|
|
110
|
+
* @param subjectId - Provider-specific unique user ID
|
|
111
|
+
* @param deviceDir - Optional directory for device.key (default: ~/.yokotoken)
|
|
112
|
+
*/
|
|
113
|
+
export declare function wrapMasterKey(db: OAuthLinksDb, masterKey: Buffer, provider: string, subjectId: string, deviceDir?: string): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Unwrap the master key using an OAuth identity.
|
|
116
|
+
*
|
|
117
|
+
* This is the main entry point for OAuth-based vault unlock.
|
|
118
|
+
* Called after the user authenticates via OAuth.
|
|
119
|
+
*
|
|
120
|
+
* @param db - sql.js database instance (from VaultEngine.getDb().getRawDb())
|
|
121
|
+
* @param provider - OAuth provider name (e.g., 'google', 'github')
|
|
122
|
+
* @param subjectId - Provider-specific unique user ID
|
|
123
|
+
* @param deviceDir - Optional directory for device.key (default: ~/.yokotoken)
|
|
124
|
+
* @returns The decrypted vault master key (32 bytes)
|
|
125
|
+
* @throws KeyUnwrapError if decryption fails (wrong identity)
|
|
126
|
+
* @throws DeviceSecretMissingError if device.key is not found
|
|
127
|
+
* @throws Error if no link exists for the given provider+subject_id
|
|
128
|
+
*/
|
|
129
|
+
export declare function unwrapMasterKey(db: OAuthLinksDb, provider: string, subjectId: string, deviceDir?: string): Promise<Buffer>;
|
|
130
|
+
/**
|
|
131
|
+
* List all linked OAuth providers for the current vault.
|
|
132
|
+
*
|
|
133
|
+
* @param db - sql.js database instance
|
|
134
|
+
* @returns Array of link rows (provider, subject_id, created_at, last_used_at)
|
|
135
|
+
*/
|
|
136
|
+
export declare function listOAuthLinks(db: OAuthLinksDb): OAuthLinkRow[];
|
|
137
|
+
/**
|
|
138
|
+
* Remove an OAuth link for a provider.
|
|
139
|
+
*
|
|
140
|
+
* @param db - sql.js database instance
|
|
141
|
+
* @param provider - OAuth provider name
|
|
142
|
+
* @param subjectId - Optional subject ID (if omitted, removes all links for the provider)
|
|
143
|
+
* @returns true if a row was deleted
|
|
144
|
+
*/
|
|
145
|
+
export declare function removeOAuthLink(db: OAuthLinksDb, provider: string, subjectId?: string): boolean;
|
|
146
|
+
//# sourceMappingURL=key-wrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-wrap.d.ts","sourceRoot":"","sources":["../../src/oauth/key-wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AASH,2BAA2B;AAC3B,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAWnC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC3C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACpB,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAClC,IAAI,IAAI,OAAO,CAAC;QAChB,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACvC,IAAI,IAAI,IAAI,CAAC;KACd,CAAC;CACH;AAiBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAa1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,CAaR;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBhD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,EAAE,EAAE,YAAY,EAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAgDjB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,YAAY,GAAG,YAAY,EAAE,CAwB/D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAyBT"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth key wrapping — encrypt/decrypt the vault master key using an
|
|
3
|
+
* OAuth-derived wrap key.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. User authenticates via OAuth (gets provider + subject_id)
|
|
7
|
+
* 2. Wrap key = HKDF-SHA256(ikm=device_secret, salt=random, info=provider+subject_id)
|
|
8
|
+
* 3. Wrapped key = XChaCha20-Poly1305(plaintext=master_key, key=wrap_key)
|
|
9
|
+
* 4. Stored in SQLite oauth_links table alongside salt and nonce
|
|
10
|
+
*
|
|
11
|
+
* Unwrap flow:
|
|
12
|
+
* 1. User authenticates via OAuth (gets provider + subject_id)
|
|
13
|
+
* 2. Look up row in oauth_links
|
|
14
|
+
* 3. Re-derive wrap key from device_secret + stored salt
|
|
15
|
+
* 4. Decrypt wrapped_key → master_key
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { hkdfSync, randomBytes } from 'node:crypto';
|
|
20
|
+
import { readDeviceSecret, generateDeviceSecret } from './device-secret.js';
|
|
21
|
+
// Libsodium is loaded via yokotoken's transitive dependency.
|
|
22
|
+
// We import it the same way yokotoken's crypto module does.
|
|
23
|
+
import sodium from 'libsodium-wrappers-sumo';
|
|
24
|
+
/** Key/nonce/salt sizes */
|
|
25
|
+
export const WRAP_KEY_BYTES = 32;
|
|
26
|
+
export const WRAP_SALT_BYTES = 32;
|
|
27
|
+
export const WRAP_NONCE_BYTES = 24; // XChaCha20-Poly1305
|
|
28
|
+
/** Ensure libsodium WASM is initialized. */
|
|
29
|
+
let sodiumReady = false;
|
|
30
|
+
async function ensureSodium() {
|
|
31
|
+
if (!sodiumReady) {
|
|
32
|
+
await sodium.ready;
|
|
33
|
+
sodiumReady = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Execute a parameterized query and return a single row as an object.
|
|
38
|
+
* Returns an empty object if no rows match.
|
|
39
|
+
*/
|
|
40
|
+
function queryOne(db, sql, params) {
|
|
41
|
+
const stmt = db.prepare(sql);
|
|
42
|
+
stmt.bind(params);
|
|
43
|
+
let result = {};
|
|
44
|
+
if (stmt.step()) {
|
|
45
|
+
result = stmt.getAsObject();
|
|
46
|
+
}
|
|
47
|
+
stmt.free();
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Initialize the oauth_links table if it does not exist.
|
|
52
|
+
*
|
|
53
|
+
* Called once when the key-wrap module is first used against a database.
|
|
54
|
+
*/
|
|
55
|
+
export function initOAuthLinksTable(db) {
|
|
56
|
+
db.exec(`
|
|
57
|
+
CREATE TABLE IF NOT EXISTS oauth_links (
|
|
58
|
+
provider TEXT NOT NULL,
|
|
59
|
+
subject_id TEXT NOT NULL,
|
|
60
|
+
wrap_salt BLOB NOT NULL,
|
|
61
|
+
wrapped_key BLOB NOT NULL,
|
|
62
|
+
nonce BLOB NOT NULL,
|
|
63
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
64
|
+
last_used_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
65
|
+
PRIMARY KEY (provider, subject_id)
|
|
66
|
+
)
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Derive a 32-byte wrap key using HKDF-SHA256.
|
|
71
|
+
*
|
|
72
|
+
* @param deviceSecret - 32-byte device secret (IKM)
|
|
73
|
+
* @param wrapSalt - 32-byte random salt (unique per link)
|
|
74
|
+
* @param provider - OAuth provider name (e.g., 'google')
|
|
75
|
+
* @param subjectId - Provider-specific user ID
|
|
76
|
+
* @returns 32-byte wrap key
|
|
77
|
+
*/
|
|
78
|
+
export function deriveWrapKey(deviceSecret, wrapSalt, provider, subjectId) {
|
|
79
|
+
// info = "unotoken-oauth:" + provider + ":" + subjectId
|
|
80
|
+
const info = `unotoken-oauth:${provider}:${subjectId}`;
|
|
81
|
+
const key = hkdfSync('sha256', deviceSecret, // IKM (input keying material)
|
|
82
|
+
wrapSalt, // salt
|
|
83
|
+
info, // info/context string
|
|
84
|
+
WRAP_KEY_BYTES);
|
|
85
|
+
return Buffer.from(key);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Wrap (encrypt) the master key with an OAuth-derived wrap key.
|
|
89
|
+
*
|
|
90
|
+
* Uses XChaCha20-Poly1305 for authenticated encryption.
|
|
91
|
+
*
|
|
92
|
+
* @param masterKey - The vault master key to wrap (32 bytes)
|
|
93
|
+
* @param wrapKey - The HKDF-derived wrap key (32 bytes)
|
|
94
|
+
* @returns Object containing wrapped (encrypted) key and the nonce used
|
|
95
|
+
*/
|
|
96
|
+
export async function encryptMasterKey(masterKey, wrapKey) {
|
|
97
|
+
await ensureSodium();
|
|
98
|
+
const nonce = sodium.randombytes_buf(WRAP_NONCE_BYTES);
|
|
99
|
+
const ciphertext = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(new Uint8Array(masterKey), null, // no additional data
|
|
100
|
+
null, // nsec (unused, always null)
|
|
101
|
+
nonce, new Uint8Array(wrapKey));
|
|
102
|
+
return {
|
|
103
|
+
wrappedKey: Buffer.from(ciphertext),
|
|
104
|
+
nonce: Buffer.from(nonce),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Unwrap (decrypt) the master key with an OAuth-derived wrap key.
|
|
109
|
+
*
|
|
110
|
+
* Uses XChaCha20-Poly1305 for authenticated decryption.
|
|
111
|
+
*
|
|
112
|
+
* @param wrappedKey - The encrypted master key
|
|
113
|
+
* @param nonce - The nonce used during encryption
|
|
114
|
+
* @param wrapKey - The HKDF-derived wrap key (32 bytes)
|
|
115
|
+
* @returns The decrypted master key (32 bytes)
|
|
116
|
+
* @throws Error if the wrap key is wrong or data is corrupted
|
|
117
|
+
*/
|
|
118
|
+
export async function decryptMasterKey(wrappedKey, nonce, wrapKey) {
|
|
119
|
+
await ensureSodium();
|
|
120
|
+
try {
|
|
121
|
+
const plaintext = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, // nsec (unused)
|
|
122
|
+
new Uint8Array(wrappedKey), null, // no additional data
|
|
123
|
+
new Uint8Array(nonce), new Uint8Array(wrapKey));
|
|
124
|
+
return Buffer.from(plaintext);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
throw new KeyUnwrapError('Failed to unwrap master key: wrong identity or corrupted data. ' +
|
|
128
|
+
'The OAuth account used may not match the one that was linked, ' +
|
|
129
|
+
'or the device.key file may have changed. ' +
|
|
130
|
+
'Use your passphrase to unlock and re-link with \'unotoken auth link <provider>\'.');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Error thrown when key unwrapping fails.
|
|
135
|
+
*/
|
|
136
|
+
export class KeyUnwrapError extends Error {
|
|
137
|
+
constructor(message) {
|
|
138
|
+
super(message);
|
|
139
|
+
this.name = 'KeyUnwrapError';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// ─── High-level wrap/unwrap operations ───────────────────────────────
|
|
143
|
+
/**
|
|
144
|
+
* Wrap the master key for an OAuth identity and store in the database.
|
|
145
|
+
*
|
|
146
|
+
* This is the main entry point for linking an OAuth identity.
|
|
147
|
+
* Called after the user authenticates via OAuth while the vault is unlocked.
|
|
148
|
+
*
|
|
149
|
+
* @param db - sql.js database instance (from VaultEngine.getDb().getRawDb())
|
|
150
|
+
* @param masterKey - The current vault master key (32 bytes)
|
|
151
|
+
* @param provider - OAuth provider name (e.g., 'google', 'github')
|
|
152
|
+
* @param subjectId - Provider-specific unique user ID
|
|
153
|
+
* @param deviceDir - Optional directory for device.key (default: ~/.yokotoken)
|
|
154
|
+
*/
|
|
155
|
+
export async function wrapMasterKey(db, masterKey, provider, subjectId, deviceDir) {
|
|
156
|
+
// Ensure table exists
|
|
157
|
+
initOAuthLinksTable(db);
|
|
158
|
+
// Generate or read device secret
|
|
159
|
+
const deviceSecret = generateDeviceSecret(deviceDir);
|
|
160
|
+
// Generate random salt for this link
|
|
161
|
+
const wrapSalt = randomBytes(WRAP_SALT_BYTES);
|
|
162
|
+
// Derive wrap key via HKDF
|
|
163
|
+
const wrapKey = deriveWrapKey(deviceSecret, wrapSalt, provider, subjectId);
|
|
164
|
+
// Encrypt master key
|
|
165
|
+
const { wrappedKey, nonce } = await encryptMasterKey(masterKey, wrapKey);
|
|
166
|
+
// Securely zero the wrap key
|
|
167
|
+
try {
|
|
168
|
+
await ensureSodium();
|
|
169
|
+
sodium.memzero(new Uint8Array(wrapKey.buffer, wrapKey.byteOffset, wrapKey.byteLength));
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Best-effort secure zeroing
|
|
173
|
+
}
|
|
174
|
+
const now = new Date().toISOString();
|
|
175
|
+
// Upsert into oauth_links (replace if provider+subject_id already linked)
|
|
176
|
+
db.run(`INSERT OR REPLACE INTO oauth_links
|
|
177
|
+
(provider, subject_id, wrap_salt, wrapped_key, nonce, created_at, last_used_at)
|
|
178
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [provider, subjectId, wrapSalt, wrappedKey, nonce, now, now]);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Unwrap the master key using an OAuth identity.
|
|
182
|
+
*
|
|
183
|
+
* This is the main entry point for OAuth-based vault unlock.
|
|
184
|
+
* Called after the user authenticates via OAuth.
|
|
185
|
+
*
|
|
186
|
+
* @param db - sql.js database instance (from VaultEngine.getDb().getRawDb())
|
|
187
|
+
* @param provider - OAuth provider name (e.g., 'google', 'github')
|
|
188
|
+
* @param subjectId - Provider-specific unique user ID
|
|
189
|
+
* @param deviceDir - Optional directory for device.key (default: ~/.yokotoken)
|
|
190
|
+
* @returns The decrypted vault master key (32 bytes)
|
|
191
|
+
* @throws KeyUnwrapError if decryption fails (wrong identity)
|
|
192
|
+
* @throws DeviceSecretMissingError if device.key is not found
|
|
193
|
+
* @throws Error if no link exists for the given provider+subject_id
|
|
194
|
+
*/
|
|
195
|
+
export async function unwrapMasterKey(db, provider, subjectId, deviceDir) {
|
|
196
|
+
// Ensure table exists
|
|
197
|
+
initOAuthLinksTable(db);
|
|
198
|
+
// Read device secret (throws DeviceSecretMissingError if missing)
|
|
199
|
+
const deviceSecret = readDeviceSecret(deviceDir);
|
|
200
|
+
// Look up the link
|
|
201
|
+
const row = queryOne(db, `SELECT wrap_salt, wrapped_key, nonce FROM oauth_links
|
|
202
|
+
WHERE provider = ? AND subject_id = ?`, [provider, subjectId]);
|
|
203
|
+
if (!row || !row.wrap_salt) {
|
|
204
|
+
throw new Error(`No OAuth link found for provider '${provider}' with subject '${subjectId}'. ` +
|
|
205
|
+
`Run 'unotoken auth link ${provider}' while the vault is unlocked to create a link.`);
|
|
206
|
+
}
|
|
207
|
+
// Extract stored values
|
|
208
|
+
const wrapSalt = Buffer.from(row.wrap_salt);
|
|
209
|
+
const wrappedKey = Buffer.from(row.wrapped_key);
|
|
210
|
+
const nonce = Buffer.from(row.nonce);
|
|
211
|
+
// Re-derive wrap key from device secret + stored salt
|
|
212
|
+
const wrapKey = deriveWrapKey(deviceSecret, wrapSalt, provider, subjectId);
|
|
213
|
+
// Decrypt master key
|
|
214
|
+
const masterKey = await decryptMasterKey(wrappedKey, nonce, wrapKey);
|
|
215
|
+
// Update last_used_at
|
|
216
|
+
db.run(`UPDATE oauth_links SET last_used_at = ? WHERE provider = ? AND subject_id = ?`, [new Date().toISOString(), provider, subjectId]);
|
|
217
|
+
// Securely zero the wrap key
|
|
218
|
+
try {
|
|
219
|
+
await ensureSodium();
|
|
220
|
+
sodium.memzero(new Uint8Array(wrapKey.buffer, wrapKey.byteOffset, wrapKey.byteLength));
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Best-effort secure zeroing
|
|
224
|
+
}
|
|
225
|
+
return masterKey;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* List all linked OAuth providers for the current vault.
|
|
229
|
+
*
|
|
230
|
+
* @param db - sql.js database instance
|
|
231
|
+
* @returns Array of link rows (provider, subject_id, created_at, last_used_at)
|
|
232
|
+
*/
|
|
233
|
+
export function listOAuthLinks(db) {
|
|
234
|
+
initOAuthLinksTable(db);
|
|
235
|
+
const links = [];
|
|
236
|
+
const stmt = db.prepare(`SELECT provider, subject_id, wrap_salt, wrapped_key, nonce, created_at, last_used_at
|
|
237
|
+
FROM oauth_links ORDER BY created_at ASC`);
|
|
238
|
+
while (stmt.step()) {
|
|
239
|
+
const row = stmt.getAsObject();
|
|
240
|
+
links.push({
|
|
241
|
+
provider: row.provider,
|
|
242
|
+
subject_id: row.subject_id,
|
|
243
|
+
wrap_salt: Buffer.from(row.wrap_salt),
|
|
244
|
+
wrapped_key: Buffer.from(row.wrapped_key),
|
|
245
|
+
nonce: Buffer.from(row.nonce),
|
|
246
|
+
created_at: row.created_at,
|
|
247
|
+
last_used_at: row.last_used_at,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
stmt.free();
|
|
251
|
+
return links;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Remove an OAuth link for a provider.
|
|
255
|
+
*
|
|
256
|
+
* @param db - sql.js database instance
|
|
257
|
+
* @param provider - OAuth provider name
|
|
258
|
+
* @param subjectId - Optional subject ID (if omitted, removes all links for the provider)
|
|
259
|
+
* @returns true if a row was deleted
|
|
260
|
+
*/
|
|
261
|
+
export function removeOAuthLink(db, provider, subjectId) {
|
|
262
|
+
initOAuthLinksTable(db);
|
|
263
|
+
// Count before delete
|
|
264
|
+
const before = queryOne(db, subjectId
|
|
265
|
+
? `SELECT COUNT(*) as cnt FROM oauth_links WHERE provider = ? AND subject_id = ?`
|
|
266
|
+
: `SELECT COUNT(*) as cnt FROM oauth_links WHERE provider = ?`, subjectId ? [provider, subjectId] : [provider]);
|
|
267
|
+
if (subjectId) {
|
|
268
|
+
db.run(`DELETE FROM oauth_links WHERE provider = ? AND subject_id = ?`, [provider, subjectId]);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
db.run(`DELETE FROM oauth_links WHERE provider = ?`, [provider]);
|
|
272
|
+
}
|
|
273
|
+
return before.cnt > 0;
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=key-wrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-wrap.js","sourceRoot":"","sources":["../../src/oauth/key-wrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) utilities for OAuth 2.0.
|
|
3
|
+
*
|
|
4
|
+
* Uses Node's built-in crypto module — no external dependencies.
|
|
5
|
+
* Implements S256 method (SHA-256 of verifier as challenge).
|
|
6
|
+
*
|
|
7
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636
|
|
8
|
+
*/
|
|
9
|
+
export interface PKCEPair {
|
|
10
|
+
/** Random high-entropy string (43-128 chars, unreserved URI characters) */
|
|
11
|
+
verifier: string;
|
|
12
|
+
/** Base64url-encoded SHA-256 hash of the verifier */
|
|
13
|
+
challenge: string;
|
|
14
|
+
/** Always 'S256' */
|
|
15
|
+
method: 'S256';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate a PKCE verifier and challenge pair.
|
|
19
|
+
*
|
|
20
|
+
* The verifier is 64 bytes of random data, base64url-encoded (86 chars).
|
|
21
|
+
* The challenge is the SHA-256 hash of the verifier, base64url-encoded.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generatePKCE(): PKCEPair;
|
|
24
|
+
/**
|
|
25
|
+
* Compute the S256 challenge from a verifier.
|
|
26
|
+
* Useful for verification in tests.
|
|
27
|
+
*/
|
|
28
|
+
export declare function computeChallenge(verifier: string): string;
|
|
29
|
+
//# sourceMappingURL=pkce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../src/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,QAAQ;IACvB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,QAAQ,CAIvC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) utilities for OAuth 2.0.
|
|
3
|
+
*
|
|
4
|
+
* Uses Node's built-in crypto module — no external dependencies.
|
|
5
|
+
* Implements S256 method (SHA-256 of verifier as challenge).
|
|
6
|
+
*
|
|
7
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636
|
|
8
|
+
*/
|
|
9
|
+
import { randomBytes, createHash } from 'node:crypto';
|
|
10
|
+
/**
|
|
11
|
+
* Generate a PKCE verifier and challenge pair.
|
|
12
|
+
*
|
|
13
|
+
* The verifier is 64 bytes of random data, base64url-encoded (86 chars).
|
|
14
|
+
* The challenge is the SHA-256 hash of the verifier, base64url-encoded.
|
|
15
|
+
*/
|
|
16
|
+
export function generatePKCE() {
|
|
17
|
+
const verifier = base64url(randomBytes(64));
|
|
18
|
+
const challenge = base64url(createHash('sha256').update(verifier).digest());
|
|
19
|
+
return { verifier, challenge, method: 'S256' };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Compute the S256 challenge from a verifier.
|
|
23
|
+
* Useful for verification in tests.
|
|
24
|
+
*/
|
|
25
|
+
export function computeChallenge(verifier) {
|
|
26
|
+
return base64url(createHash('sha256').update(verifier).digest());
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Base64url encoding (RFC 4648 section 5) without padding.
|
|
30
|
+
*/
|
|
31
|
+
function base64url(buf) {
|
|
32
|
+
return buf.toString('base64url');
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWtD;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AACnE,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth provider interface for unotoken.
|
|
3
|
+
*
|
|
4
|
+
* Each provider (Google, GitHub, etc.) implements this interface to
|
|
5
|
+
* plug into the generic OAuthFlow engine. The flow engine handles
|
|
6
|
+
* PKCE, callback server, and browser opening — providers just define
|
|
7
|
+
* their endpoints and token verification logic.
|
|
8
|
+
*/
|
|
9
|
+
import type { PKCEPair } from './pkce.js';
|
|
10
|
+
/**
|
|
11
|
+
* Result of a successful OAuth authentication.
|
|
12
|
+
*/
|
|
13
|
+
export interface OAuthIdentity {
|
|
14
|
+
/** Provider name (e.g., 'google', 'github') */
|
|
15
|
+
provider: string;
|
|
16
|
+
/** Provider-specific unique user ID (e.g., Google sub, GitHub user ID) */
|
|
17
|
+
subjectId: string;
|
|
18
|
+
/** User's email address (if available) */
|
|
19
|
+
email?: string;
|
|
20
|
+
/** User's display name (if available) */
|
|
21
|
+
name?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Token response from the OAuth token endpoint.
|
|
25
|
+
*/
|
|
26
|
+
export interface TokenResponse {
|
|
27
|
+
access_token: string;
|
|
28
|
+
token_type: string;
|
|
29
|
+
expires_in?: number;
|
|
30
|
+
refresh_token?: string;
|
|
31
|
+
id_token?: string;
|
|
32
|
+
scope?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Provider interface — each OAuth provider must implement this.
|
|
36
|
+
*/
|
|
37
|
+
export interface OAuthProvider {
|
|
38
|
+
/** Provider identifier (e.g., 'google', 'github') */
|
|
39
|
+
readonly name: string;
|
|
40
|
+
/** OAuth client ID */
|
|
41
|
+
readonly clientId: string;
|
|
42
|
+
/** Requested scopes */
|
|
43
|
+
readonly scopes: string[];
|
|
44
|
+
/**
|
|
45
|
+
* Build the authorization URL for the browser redirect.
|
|
46
|
+
*
|
|
47
|
+
* @param redirectUri - The localhost callback URL (e.g., http://localhost:12345/callback)
|
|
48
|
+
* @param state - CSRF protection state parameter
|
|
49
|
+
* @param pkce - PKCE verifier/challenge pair
|
|
50
|
+
* @returns Full authorization URL to open in the browser
|
|
51
|
+
*/
|
|
52
|
+
authorizeUrl(redirectUri: string, state: string, pkce: PKCEPair): string;
|
|
53
|
+
/** Token endpoint URL */
|
|
54
|
+
readonly tokenEndpoint: string;
|
|
55
|
+
/**
|
|
56
|
+
* Build the body parameters for the token exchange request.
|
|
57
|
+
*
|
|
58
|
+
* Most providers use the standard OAuth2 code exchange, but some (like GitHub)
|
|
59
|
+
* require additional parameters (e.g., client_secret).
|
|
60
|
+
*
|
|
61
|
+
* @param code - Authorization code from the callback
|
|
62
|
+
* @param redirectUri - The same redirect URI used in the authorize request
|
|
63
|
+
* @param pkce - PKCE pair (verifier is sent to prove possession)
|
|
64
|
+
* @returns URLSearchParams for the POST body
|
|
65
|
+
*/
|
|
66
|
+
tokenRequestBody(code: string, redirectUri: string, pkce: PKCEPair): URLSearchParams;
|
|
67
|
+
/**
|
|
68
|
+
* Verify and extract identity from the token response.
|
|
69
|
+
*
|
|
70
|
+
* For OIDC providers (Google): verify the ID token signature, expiry, audience, issuer.
|
|
71
|
+
* For non-OIDC providers (GitHub): use the access token to call a user info endpoint.
|
|
72
|
+
*
|
|
73
|
+
* @param tokenResponse - Raw token response from the token endpoint
|
|
74
|
+
* @returns Verified identity
|
|
75
|
+
* @throws Error if verification fails
|
|
76
|
+
*/
|
|
77
|
+
verifyIdentity(tokenResponse: TokenResponse): Promise<OAuthIdentity>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/oauth/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,sBAAsB;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,uBAAuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAE1B;;;;;;;OAOG;IACH,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAEzE,yBAAyB;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,eAAe,CAAC;IAErF;;;;;;;;;OASG;IACH,cAAc,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACtE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth provider interface for unotoken.
|
|
3
|
+
*
|
|
4
|
+
* Each provider (Google, GitHub, etc.) implements this interface to
|
|
5
|
+
* plug into the generic OAuthFlow engine. The flow engine handles
|
|
6
|
+
* PKCE, callback server, and browser opening — providers just define
|
|
7
|
+
* their endpoints and token verification logic.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=provider.js.map
|