@modelprofile.com/authswitch 8.1.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/authority-contract.d.ts +73 -0
- package/dist_ts/authority-contract.js +55 -2
- package/dist_ts/authority-import-contract.d.ts +8 -4
- package/dist_ts/authority-import-contract.js +12 -13
- package/dist_ts/authority-paths.d.ts +37 -0
- package/dist_ts/authority-paths.js +46 -0
- package/dist_ts/authority-runtime-contract.d.ts +11 -1
- package/dist_ts/classes.authoritybroker.d.ts +9 -0
- package/dist_ts/classes.authoritybroker.js +72 -11
- package/dist_ts/classes.authorityclient.d.ts +17 -11
- package/dist_ts/classes.authorityclient.js +47 -22
- package/dist_ts/classes.authoritydaemon.d.ts +22 -3
- package/dist_ts/classes.authoritydaemon.js +106 -59
- package/dist_ts/classes.authoritydatabase.d.ts +9 -1
- package/dist_ts/classes.authoritydatabase.js +39 -13
- package/dist_ts/classes.authorityimport.d.ts +33 -6
- package/dist_ts/classes.authorityimport.js +102 -25
- package/dist_ts/classes.authoritymodels.d.ts +4 -1
- package/dist_ts/classes.authoritymodels.js +15 -3
- package/dist_ts/classes.authoritypreuse.js +8 -3
- package/dist_ts/classes.authorityservice.d.ts +20 -11
- package/dist_ts/classes.authorityservice.js +37 -23
- package/dist_ts/classes.claudeauthority.js +19 -11
- package/dist_ts/classes.claudenative.d.ts +63 -3
- package/dist_ts/classes.claudenative.js +68 -8
- package/dist_ts/classes.cli.d.ts +10 -2
- package/dist_ts/classes.cli.js +12 -4
- package/dist_ts/classes.codexmanaged.d.ts +8 -0
- package/dist_ts/classes.codexmanaged.js +19 -12
- package/dist_ts/classes.legacyfence.d.ts +53 -0
- package/dist_ts/classes.legacyfence.js +189 -0
- package/dist_ts/classes.operations.d.ts +15 -3
- package/dist_ts/classes.operations.js +22 -4
- package/dist_ts/classes.service.d.ts +21 -2
- package/dist_ts/classes.service.js +35 -8
- package/dist_ts/classes.tui.d.ts +2 -1
- package/dist_ts/classes.tui.js +3 -2
- package/dist_ts/ts_migration/0003_claude_handoff_proof.d.ts +13 -0
- package/dist_ts/ts_migration/0003_claude_handoff_proof.js +20 -0
- package/dist_ts/ts_migration/index.js +3 -1
- package/dist_ts/ts_migration/legacysources/authswitchstores.js +5 -2
- package/dist_ts/ts_migration/legacysources/nativestores.js +15 -8
- package/dist_ts/ts_migration/legacysources/shared.d.ts +3 -2
- package/dist_ts/ts_migration/legacysources/shared.js +3 -5
- package/package.json +11 -7
- package/readme.md +188 -13
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/authority-contract.ts +125 -0
- package/ts/authority-import-contract.ts +12 -11
- package/ts/authority-paths.ts +69 -0
- package/ts/authority-runtime-contract.ts +11 -1
- package/ts/classes.authoritybroker.ts +70 -11
- package/ts/classes.authorityclient.ts +59 -21
- package/ts/classes.authoritydaemon.ts +116 -60
- package/ts/classes.authoritydatabase.ts +39 -12
- package/ts/classes.authorityimport.ts +131 -26
- package/ts/classes.authoritymodels.ts +11 -2
- package/ts/classes.authoritypreuse.ts +7 -1
- package/ts/classes.authorityservice.ts +42 -30
- package/ts/classes.claudeauthority.ts +22 -10
- package/ts/classes.claudenative.ts +104 -10
- package/ts/classes.cli.ts +14 -3
- package/ts/classes.codexmanaged.ts +18 -7
- package/ts/classes.legacyfence.ts +219 -0
- package/ts/classes.operations.ts +22 -3
- package/ts/classes.service.ts +45 -8
- package/ts/classes.tui.ts +3 -1
- package/ts/ts_migration/0003_claude_handoff_proof.ts +19 -0
- package/ts/ts_migration/index.ts +2 -0
- package/ts/ts_migration/legacysources/authswitchstores.ts +4 -1
- package/ts/ts_migration/legacysources/nativestores.ts +15 -7
- package/ts/ts_migration/legacysources/shared.ts +4 -6
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@modelprofile.com/authswitch',
|
|
6
|
-
version: '
|
|
6
|
+
version: '9.0.0',
|
|
7
7
|
description: 'Manage Codex, OpenCode and Claude Code accounts with guided switching and live usage status'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSw4QkFBOEI7SUFDcEMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDZGQUE2RjtDQUMzRyxDQUFBIn0=
|
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
import type { ITypedRequest } from '@api.global/typedrequest-interfaces';
|
|
2
2
|
import type { IAuthSwitchUsageSnapshot } from './classes.authorityusage.js';
|
|
3
3
|
export type { IAuthSwitchUsageSnapshot } from './classes.authorityusage.js';
|
|
4
|
+
/**
|
|
5
|
+
* Why the authority declined, as a closed set a client branches on without reading the text.
|
|
6
|
+
*
|
|
7
|
+
* A refusal is an answer: the daemon did the check, nothing was left half-done, and the message says what
|
|
8
|
+
* the owner does instead. Everything else stays a fault whose text the transport replaces, and a caller
|
|
9
|
+
* must never present a fault as if it said what to do next.
|
|
10
|
+
*/
|
|
11
|
+
export type TAuthSwitchRefusalCode = 'authority_closing' | 'not_found' | 'account_changed' | 'account_busy' | 'login_unavailable'
|
|
12
|
+
/** A runtime capability that is missing, wrong or superseded: deliberately one answer, and one repair. */
|
|
13
|
+
| 'binding_unauthorized'
|
|
14
|
+
/** The authority holds this login, so the legacy path that used to move it no longer may. */
|
|
15
|
+
| 'authority_holds_login'
|
|
16
|
+
/** An authority is installed on this host and is not answering, so nothing may assume it holds nothing. */
|
|
17
|
+
| 'authority_unavailable' | 'native_owner_holds_login' | 'claude_home_unregistered' | 'claude_handoff_pending' | 'claude_receipt_missing' | 'import_refusal';
|
|
18
|
+
/** The marker the importer has published since 8.1.0; it keeps its own value on the wire. */
|
|
19
|
+
export declare const authSwitchImportRefusalMarker = "authswitch_import_refusal";
|
|
20
|
+
export declare const authSwitchRefusalReason = "authswitch_refusal";
|
|
21
|
+
export type TAuthSwitchRefusalReason = typeof authSwitchRefusalReason | typeof authSwitchImportRefusalMarker;
|
|
22
|
+
/** Everything a refusal puts on the wire beside its message: two literals, never a value of its own. */
|
|
23
|
+
export interface IAuthSwitchRefusalData {
|
|
24
|
+
reason: TAuthSwitchRefusalReason;
|
|
25
|
+
code: TAuthSwitchRefusalCode;
|
|
26
|
+
}
|
|
27
|
+
/** What the owner is told when the answer is an instruction rather than a fault. */
|
|
28
|
+
export interface IAuthSwitchRefusal {
|
|
29
|
+
code: TAuthSwitchRefusalCode;
|
|
30
|
+
/** The instruction, authored by the handler that declined. */
|
|
31
|
+
instruction: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* One refusal, thrown by a handler and turned into a marked answer by the router that owns the wire.
|
|
35
|
+
*
|
|
36
|
+
* It accepts nothing but a code of the closed set and the text the owner is to read, and builds the marker
|
|
37
|
+
* from the code alone, so no handler can attach a value of its own to what a client branches on.
|
|
38
|
+
*/
|
|
39
|
+
export declare class AuthSwitchRefusal extends Error {
|
|
40
|
+
readonly code: TAuthSwitchRefusalCode;
|
|
41
|
+
constructor(code: TAuthSwitchRefusalCode, instruction: string);
|
|
42
|
+
/** Everything this refusal puts on the wire beside its message. */
|
|
43
|
+
get data(): IAuthSwitchRefusalData;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The refusal an error answer carries, or null when it is a fault whose text says nothing.
|
|
47
|
+
*
|
|
48
|
+
* This is the only reader of `errorData` in the package: the importer's published predicate is expressed
|
|
49
|
+
* through it, so a caller never has to know which of the two markers a route uses.
|
|
50
|
+
*
|
|
51
|
+
* 8.1.0 marked the importer's refusal with the reason alone. A per-user daemon keeps running across an
|
|
52
|
+
* upgrade until its owner restarts it, so a newer client still meets that payload, and it names the one
|
|
53
|
+
* refusal that release could answer with -- which is why it is read rather than treated as a fault.
|
|
54
|
+
*/
|
|
55
|
+
export declare const asAuthSwitchRefusal: (error: unknown) => IAuthSwitchRefusal | null;
|
|
4
56
|
/** Credential-free account management contract. Safe to import in browser code. */
|
|
5
57
|
export type TAuthSwitchLoginPurpose = 'openai_managed' | 'claude_host_native' | 'claude_container_setup' | 'opencode_native';
|
|
6
58
|
export type TAuthSwitchLoginHealth = 'ready' | 'refreshing' | 'retry_wait' | 'needs_reauth' | 'unverified' | 'pending_handoff' | 'handoff_quarantined' | 'removed';
|
|
@@ -14,6 +66,15 @@ export interface IAuthSwitchAccount {
|
|
|
14
66
|
revision: number;
|
|
15
67
|
statusObservedAt: string;
|
|
16
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Which tool refreshes a natively owned login, named rather than inferred.
|
|
71
|
+
*
|
|
72
|
+
* `owner` says that a native tool holds the login; this says which one, so a consumer presents "Claude Code"
|
|
73
|
+
* or "Codex" without deriving it from `purpose` -- a derivation that would be wrong the moment two purposes
|
|
74
|
+
* share a tool, and that belongs to the side that decides ownership in the first place. It is `null` exactly
|
|
75
|
+
* when no native tool refreshes the login: the authority does, or nothing does.
|
|
76
|
+
*/
|
|
77
|
+
export type TAuthSwitchLoginOwnerTool = 'claude_code' | 'codex' | 'opencode';
|
|
17
78
|
/** One independently owned login. Native ownership does not imply observed health. */
|
|
18
79
|
export interface IAuthSwitchLogin {
|
|
19
80
|
id: string;
|
|
@@ -21,6 +82,8 @@ export interface IAuthSwitchLogin {
|
|
|
21
82
|
providerId: string;
|
|
22
83
|
purpose: TAuthSwitchLoginPurpose;
|
|
23
84
|
owner: 'daemon' | 'claude_native' | 'legacy_native' | 'none';
|
|
85
|
+
/** The native tool that refreshes this login, or null when the authority or nobody does. */
|
|
86
|
+
ownerTool: TAuthSwitchLoginOwnerTool | null;
|
|
24
87
|
health: TAuthSwitchLoginHealth;
|
|
25
88
|
problem: 'none' | 'provider_unavailable' | 'exchange_uncertain' | 'provider_rejected' | 'native_owner';
|
|
26
89
|
grantGeneration: number;
|
|
@@ -231,6 +294,14 @@ export interface IAuthSwitchPreuseOperation {
|
|
|
231
294
|
finishedAt: string | null;
|
|
232
295
|
revision: number;
|
|
233
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Why the host could not prove that Claude Code uses its own native login.
|
|
299
|
+
*
|
|
300
|
+
* Each value names one condition of that proof, so an owner is told what to change: install the supported
|
|
301
|
+
* release, sign in to a subscriber plan, remove a credential override or a settings source, let the
|
|
302
|
+
* profile read succeed, or close the running session.
|
|
303
|
+
*/
|
|
304
|
+
export type TAuthSwitchClaudeProofFailure = 'unsupported_release' | 'override' | 'profile' | 'profile_unreadable' | 'settings' | 'subscription' | 'running_session';
|
|
234
305
|
/** Credential-free native file ownership result. It makes no claim about a running session's active request. */
|
|
235
306
|
export interface IAuthSwitchClaudeNativeHandoff {
|
|
236
307
|
id: string;
|
|
@@ -240,6 +311,8 @@ export interface IAuthSwitchClaudeNativeHandoff {
|
|
|
240
311
|
phase: 'reserved' | 'prepared' | 'committed' | 'aborted' | 'quarantined';
|
|
241
312
|
problem: 'none' | 'native_uncertain' | 'foreign_or_torn' | 'unsupported_effective_auth' | 'database_uncertain';
|
|
242
313
|
runningEffectiveAuth: 'no_scoped_sessions' | 'unsupported_effective_auth' | null;
|
|
314
|
+
/** Which condition of the native-login proof failed, when `problem` is `unsupported_effective_auth`. */
|
|
315
|
+
proofFailure?: TAuthSwitchClaudeProofFailure;
|
|
243
316
|
updatedAt: string;
|
|
244
317
|
}
|
|
245
318
|
export interface IReq_AuthSwitchSwitchClaudeNative extends ITypedRequest {
|
|
@@ -1,3 +1,56 @@
|
|
|
1
|
-
|
|
1
|
+
/** The marker the importer has published since 8.1.0; it keeps its own value on the wire. */
|
|
2
|
+
export const authSwitchImportRefusalMarker = 'authswitch_import_refusal';
|
|
3
|
+
export const authSwitchRefusalReason = 'authswitch_refusal';
|
|
4
|
+
const refusalCodes = new Set([
|
|
5
|
+
'authority_closing', 'not_found', 'account_changed', 'account_busy',
|
|
6
|
+
'login_unavailable', 'binding_unauthorized', 'native_owner_holds_login', 'claude_home_unregistered',
|
|
7
|
+
'claude_handoff_pending', 'claude_receipt_missing', 'import_refusal',
|
|
8
|
+
'authority_holds_login', 'authority_unavailable',
|
|
9
|
+
]);
|
|
10
|
+
const isRefusalCode = (value) => typeof value === 'string' && refusalCodes.has(value);
|
|
11
|
+
/** The importer keeps the marker it published; every other code shares the authority's own. */
|
|
12
|
+
const reasonOf = (code) => code === 'import_refusal' ? authSwitchImportRefusalMarker : authSwitchRefusalReason;
|
|
13
|
+
/**
|
|
14
|
+
* One refusal, thrown by a handler and turned into a marked answer by the router that owns the wire.
|
|
15
|
+
*
|
|
16
|
+
* It accepts nothing but a code of the closed set and the text the owner is to read, and builds the marker
|
|
17
|
+
* from the code alone, so no handler can attach a value of its own to what a client branches on.
|
|
18
|
+
*/
|
|
19
|
+
export class AuthSwitchRefusal extends Error {
|
|
20
|
+
code;
|
|
21
|
+
constructor(code, instruction) {
|
|
22
|
+
super(instruction);
|
|
23
|
+
this.code = code;
|
|
24
|
+
}
|
|
25
|
+
/** Everything this refusal puts on the wire beside its message. */
|
|
26
|
+
get data() { return { reason: reasonOf(this.code), code: this.code }; }
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The refusal an error answer carries, or null when it is a fault whose text says nothing.
|
|
30
|
+
*
|
|
31
|
+
* This is the only reader of `errorData` in the package: the importer's published predicate is expressed
|
|
32
|
+
* through it, so a caller never has to know which of the two markers a route uses.
|
|
33
|
+
*
|
|
34
|
+
* 8.1.0 marked the importer's refusal with the reason alone. A per-user daemon keeps running across an
|
|
35
|
+
* upgrade until its owner restarts it, so a newer client still meets that payload, and it names the one
|
|
36
|
+
* refusal that release could answer with -- which is why it is read rather than treated as a fault.
|
|
37
|
+
*/
|
|
38
|
+
export const asAuthSwitchRefusal = (error) => {
|
|
39
|
+
if (error instanceof AuthSwitchRefusal)
|
|
40
|
+
return { code: error.code, instruction: error.message };
|
|
41
|
+
if (typeof error !== 'object' || error === null || !('errorData' in error))
|
|
42
|
+
return null;
|
|
43
|
+
const data = error.errorData;
|
|
44
|
+
if (typeof data !== 'object' || data === null || !('reason' in data))
|
|
45
|
+
return null;
|
|
46
|
+
const code = 'code' in data ? data.code : undefined;
|
|
47
|
+
const instruction = 'message' in error && typeof error.message === 'string' ? error.message : '';
|
|
48
|
+
if (data.reason === authSwitchImportRefusalMarker) {
|
|
49
|
+
return code === undefined || code === 'import_refusal' ? { code: 'import_refusal', instruction } : null;
|
|
50
|
+
}
|
|
51
|
+
if (data.reason !== authSwitchRefusalReason || !isRefusalCode(code))
|
|
52
|
+
return null;
|
|
53
|
+
return { code, instruction };
|
|
54
|
+
};
|
|
2
55
|
/** Runtime capability is returned only to trusted backend callers; do not expose this response in a browser API. */
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LWNvbnRyYWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvYXV0aG9yaXR5LWNvbnRyYWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQTZCQSw2RkFBNkY7QUFDN0YsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsMkJBQTJCLENBQUM7QUFDekUsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsb0JBQW9CLENBQUM7QUFpQjVELE1BQU0sWUFBWSxHQUF3QixJQUFJLEdBQUcsQ0FBeUI7SUFDeEUsbUJBQW1CLEVBQUUsV0FBVyxFQUFFLGlCQUFpQixFQUFFLGNBQWM7SUFDbkUsbUJBQW1CLEVBQUUsc0JBQXNCLEVBQUUsMEJBQTBCLEVBQUUsMEJBQTBCO0lBQ25HLHdCQUF3QixFQUFFLHdCQUF3QixFQUFFLGdCQUFnQjtJQUNwRSx1QkFBdUIsRUFBRSx1QkFBdUI7Q0FDakQsQ0FBQyxDQUFDO0FBRUgsTUFBTSxhQUFhLEdBQUcsQ0FBQyxLQUFjLEVBQW1DLEVBQUUsQ0FDeEUsT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLFlBQVksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkQsK0ZBQStGO0FBQy9GLE1BQU0sUUFBUSxHQUFHLENBQUMsSUFBNEIsRUFBNEIsRUFBRSxDQUMxRSxJQUFJLEtBQUssZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLDZCQUE2QixDQUFDLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQztBQUV0Rjs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxpQkFBa0IsU0FBUSxLQUFLO0lBQzFCLElBQUksQ0FBeUI7SUFFN0MsWUFBWSxJQUE0QixFQUFFLFdBQW1CO1FBQzNELEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNuQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztJQUNuQixDQUFDO0lBRUQsbUVBQW1FO0lBQ25FLElBQVcsSUFBSSxLQUE2QixPQUFPLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7Q0FDdkc7QUFFRDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLEtBQWMsRUFBNkIsRUFBRTtJQUMvRSxJQUFJLEtBQUssWUFBWSxpQkFBaUI7UUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJLEVBQUUsV0FBVyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNoRyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxXQUFXLElBQUksS0FBSyxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDeEYsTUFBTSxJQUFJLEdBQVksS0FBSyxDQUFDLFNBQVMsQ0FBQztJQUN0QyxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxJQUFJLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDbEYsTUFBTSxJQUFJLEdBQVksTUFBTSxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQzdELE1BQU0sV0FBVyxHQUFHLFNBQVMsSUFBSSxLQUFLLElBQUksT0FBTyxLQUFLLENBQUMsT0FBTyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQ2pHLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyw2QkFBNkIsRUFBRSxDQUFDO1FBQ2xELE9BQU8sSUFBSSxLQUFLLFNBQVMsSUFBSSxJQUFJLEtBQUssZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLGdCQUFnQixFQUFFLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDMUcsQ0FBQztJQUNELElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyx1QkFBdUIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUNqRixPQUFPLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxDQUFDO0FBQy9CLENBQUMsQ0FBQztBQWdZRixvSEFBb0gifQ==
|
|
@@ -142,10 +142,14 @@ export type TAuthSwitchImportAction = 'none' | 'resume' | 'device_login';
|
|
|
142
142
|
/**
|
|
143
143
|
* Marks an error answer whose text is an instruction for the owner, not a report of a fault.
|
|
144
144
|
*
|
|
145
|
-
* The daemon sets it on the refusals the importer authors and on nothing else
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
145
|
+
* The daemon sets it on the refusals the importer authors and on nothing else: a refusal any other route
|
|
146
|
+
* authors carries the authority's own marker, and a failure neither of them decided answers with the
|
|
147
|
+
* transport's sanitised text, which a caller must never present as if it said what to do next. For a
|
|
148
|
+
* submit in particular, an unmarked failure means the outcome is unknown and the source must be read with
|
|
149
|
+
* `authswitch.authority.import.status` rather than submitted again.
|
|
150
|
+
*
|
|
151
|
+
* It is one code of the authority's refusal contract (`./authority-contract`), which every route now uses;
|
|
152
|
+
* this marker keeps its own value on the wire so a consumer written against 8.1.0 keeps working.
|
|
149
153
|
*/
|
|
150
154
|
export declare const authSwitchImportRefusalReason = "authswitch_import_refusal";
|
|
151
155
|
/** True when the daemon answered with an importer refusal, so `error.message` is the instruction to show. */
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
+
import { asAuthSwitchRefusal, authSwitchImportRefusalMarker } from './authority-contract.js';
|
|
1
2
|
/**
|
|
2
3
|
* Marks an error answer whose text is an instruction for the owner, not a report of a fault.
|
|
3
4
|
*
|
|
4
|
-
* The daemon sets it on the refusals the importer authors and on nothing else
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* The daemon sets it on the refusals the importer authors and on nothing else: a refusal any other route
|
|
6
|
+
* authors carries the authority's own marker, and a failure neither of them decided answers with the
|
|
7
|
+
* transport's sanitised text, which a caller must never present as if it said what to do next. For a
|
|
8
|
+
* submit in particular, an unmarked failure means the outcome is unknown and the source must be read with
|
|
9
|
+
* `authswitch.authority.import.status` rather than submitted again.
|
|
10
|
+
*
|
|
11
|
+
* It is one code of the authority's refusal contract (`./authority-contract`), which every route now uses;
|
|
12
|
+
* this marker keeps its own value on the wire so a consumer written against 8.1.0 keeps working.
|
|
8
13
|
*/
|
|
9
|
-
export const authSwitchImportRefusalReason =
|
|
14
|
+
export const authSwitchImportRefusalReason = authSwitchImportRefusalMarker;
|
|
10
15
|
/** True when the daemon answered with an importer refusal, so `error.message` is the instruction to show. */
|
|
11
|
-
export const isAuthSwitchImportRefusal = (error) =>
|
|
12
|
-
|
|
13
|
-
return false;
|
|
14
|
-
const data = error.errorData;
|
|
15
|
-
return typeof data === 'object' && data !== null && 'reason' in data
|
|
16
|
-
&& data.reason === authSwitchImportRefusalReason;
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LWltcG9ydC1jb250cmFjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2F1dGhvcml0eS1pbXBvcnQtY29udHJhY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBbUpBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRywyQkFBMkIsQ0FBQztBQUV6RSw2R0FBNkc7QUFDN0csTUFBTSxDQUFDLE1BQU0seUJBQXlCLEdBQUcsQ0FBQyxLQUFjLEVBQVcsRUFBRTtJQUNuRSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxXQUFXLElBQUksS0FBSyxDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDekYsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQztJQUM3QixPQUFPLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxJQUFJLEtBQUssSUFBSSxJQUFJLFFBQVEsSUFBSSxJQUFJO1dBQy9ELElBQUksQ0FBQyxNQUFNLEtBQUssNkJBQTZCLENBQUM7QUFDckQsQ0FBQyxDQUFDIn0=
|
|
16
|
+
export const isAuthSwitchImportRefusal = (error) => asAuthSwitchRefusal(error)?.code === 'import_refusal';
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LWltcG9ydC1jb250cmFjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2F1dGhvcml0eS1pbXBvcnQtY29udHJhY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLG1CQUFtQixFQUFFLDZCQUE2QixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFtSjdGOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsNkJBQTZCLENBQUM7QUFFM0UsNkdBQTZHO0FBQzdHLE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFHLENBQUMsS0FBYyxFQUFXLEVBQUUsQ0FDbkUsbUJBQW1CLENBQUMsS0FBSyxDQUFDLEVBQUUsSUFBSSxLQUFLLGdCQUFnQixDQUFDIn0=
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where this user's authority lives: its sockets, its store and the service unit that runs it.
|
|
3
|
+
*
|
|
4
|
+
* These rules sit in a leaf module because two sides read them. `AuthSwitchAuthorityService` installs and
|
|
5
|
+
* runs the daemon there, and the legacy fence (`./classes.legacyfence`) asks the same socket and looks for
|
|
6
|
+
* the same unit -- without importing the daemon, whose module graph reaches back to the legacy operations
|
|
7
|
+
* the fence guards.
|
|
8
|
+
*/
|
|
9
|
+
export interface IAuthSwitchAuthorityPaths {
|
|
10
|
+
runtimeDirectory: string;
|
|
11
|
+
dataDirectory: string;
|
|
12
|
+
databaseSocketPath: string;
|
|
13
|
+
authoritySocketPath: string;
|
|
14
|
+
runtimeSocketPath: string;
|
|
15
|
+
runtimeSocketDirectory: string;
|
|
16
|
+
}
|
|
17
|
+
/** The authority's sockets. */
|
|
18
|
+
export type TAuthSwitchAuthoritySocketPaths = Omit<IAuthSwitchAuthorityPaths, 'dataDirectory'>;
|
|
19
|
+
/**
|
|
20
|
+
* The sockets under one runtime directory, derived from it alone.
|
|
21
|
+
*
|
|
22
|
+
* The legacy fence asks the socket a caller states and must not depend on anything this process holds --
|
|
23
|
+
* its home or its `XDG_DATA_HOME`, which only the store's directory needs.
|
|
24
|
+
*/
|
|
25
|
+
export declare const authSwitchAuthoritySocketPaths: (runtimeDirectory: string | undefined) => TAuthSwitchAuthoritySocketPaths;
|
|
26
|
+
/** Canonical per-user paths. An explicit runtime directory is also usable by a trusted container host. */
|
|
27
|
+
export declare const resolveAuthSwitchAuthorityPaths: (runtimeDirectory?: string | undefined) => IAuthSwitchAuthorityPaths;
|
|
28
|
+
/** The user service `authswitch authority service install` writes. */
|
|
29
|
+
export declare const authSwitchAuthorityUnitName = "authswitch-authority.service";
|
|
30
|
+
/**
|
|
31
|
+
* The directory that unit is installed into, from a stated environment and home directory.
|
|
32
|
+
*
|
|
33
|
+
* It is the rule smartdaemon applies to a user unit when it is given no directory --
|
|
34
|
+
* `$XDG_DATA_HOME/systemd/user`, default `~/.local/share/systemd/user` -- stated here so the installer passes
|
|
35
|
+
* it explicitly and the fence derives it from the locations its caller states rather than from this process.
|
|
36
|
+
*/
|
|
37
|
+
export declare const authSwitchAuthorityUnitDirectory: (env: NodeJS.ProcessEnv, homeDirectory: string) => string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* The sockets under one runtime directory, derived from it alone.
|
|
4
|
+
*
|
|
5
|
+
* The legacy fence asks the socket a caller states and must not depend on anything this process holds --
|
|
6
|
+
* its home or its `XDG_DATA_HOME`, which only the store's directory needs.
|
|
7
|
+
*/
|
|
8
|
+
export const authSwitchAuthoritySocketPaths = (runtimeDirectory) => {
|
|
9
|
+
if (!runtimeDirectory || !plugins.path.isAbsolute(runtimeDirectory)) {
|
|
10
|
+
throw new Error('A private XDG_RUNTIME_DIR is required for authswitch authority.');
|
|
11
|
+
}
|
|
12
|
+
const socketDirectory = plugins.path.join(runtimeDirectory, 'authswitch');
|
|
13
|
+
const runtimeSocketDirectory = plugins.path.join(socketDirectory, 'runtime');
|
|
14
|
+
return {
|
|
15
|
+
runtimeDirectory,
|
|
16
|
+
databaseSocketPath: plugins.path.join(socketDirectory, 'internal', 'db.sock'),
|
|
17
|
+
authoritySocketPath: plugins.path.join(socketDirectory, 'management', 'authority.sock'),
|
|
18
|
+
runtimeSocketPath: plugins.path.join(runtimeSocketDirectory, 'runtime.sock'),
|
|
19
|
+
runtimeSocketDirectory,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/** Canonical per-user paths. An explicit runtime directory is also usable by a trusted container host. */
|
|
23
|
+
export const resolveAuthSwitchAuthorityPaths = (runtimeDirectory = process.env.XDG_RUNTIME_DIR) => {
|
|
24
|
+
const sockets = authSwitchAuthoritySocketPaths(runtimeDirectory);
|
|
25
|
+
const home = plugins.os.userInfo().homedir;
|
|
26
|
+
const dataHome = process.env.XDG_DATA_HOME || plugins.path.join(home, '.local/share');
|
|
27
|
+
if (!plugins.path.isAbsolute(dataHome))
|
|
28
|
+
throw new Error('XDG_DATA_HOME must be an absolute path.');
|
|
29
|
+
return { ...sockets, dataDirectory: plugins.path.join(dataHome, 'authswitch', 'authority') };
|
|
30
|
+
};
|
|
31
|
+
/** The user service `authswitch authority service install` writes. */
|
|
32
|
+
export const authSwitchAuthorityUnitName = 'authswitch-authority.service';
|
|
33
|
+
/**
|
|
34
|
+
* The directory that unit is installed into, from a stated environment and home directory.
|
|
35
|
+
*
|
|
36
|
+
* It is the rule smartdaemon applies to a user unit when it is given no directory --
|
|
37
|
+
* `$XDG_DATA_HOME/systemd/user`, default `~/.local/share/systemd/user` -- stated here so the installer passes
|
|
38
|
+
* it explicitly and the fence derives it from the locations its caller states rather than from this process.
|
|
39
|
+
*/
|
|
40
|
+
export const authSwitchAuthorityUnitDirectory = (env, homeDirectory) => {
|
|
41
|
+
const dataHome = env.XDG_DATA_HOME || plugins.path.join(homeDirectory, '.local/share');
|
|
42
|
+
if (!plugins.path.isAbsolute(dataHome))
|
|
43
|
+
throw new Error('XDG_DATA_HOME must be an absolute path.');
|
|
44
|
+
return plugins.path.join(dataHome, 'systemd/user');
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LXBhdGhzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvYXV0aG9yaXR5LXBhdGhzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBdUJ4Qzs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxNQUFNLDhCQUE4QixHQUFHLENBQzVDLGdCQUFvQyxFQUFtQyxFQUFFO0lBQ3pFLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztRQUNwRSxNQUFNLElBQUksS0FBSyxDQUFDLGlFQUFpRSxDQUFDLENBQUM7SUFDckYsQ0FBQztJQUNELE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQzFFLE1BQU0sc0JBQXNCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQzdFLE9BQU87UUFDTCxnQkFBZ0I7UUFDaEIsa0JBQWtCLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLFVBQVUsRUFBRSxTQUFTLENBQUM7UUFDN0UsbUJBQW1CLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLFlBQVksRUFBRSxnQkFBZ0IsQ0FBQztRQUN2RixpQkFBaUIsRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxjQUFjLENBQUM7UUFDNUUsc0JBQXNCO0tBQ3ZCLENBQUM7QUFDSixDQUFDLENBQUM7QUFFRiwwR0FBMEc7QUFDMUcsTUFBTSxDQUFDLE1BQU0sK0JBQStCLEdBQUcsQ0FBQyxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGVBQWUsRUFBNkIsRUFBRTtJQUMzSCxNQUFNLE9BQU8sR0FBRyw4QkFBOEIsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ2pFLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsT0FBTyxDQUFDO0lBQzNDLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsQ0FBQztJQUN0RixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDO1FBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyx5Q0FBeUMsQ0FBQyxDQUFDO0lBQ25HLE9BQU8sRUFBRSxHQUFHLE9BQU8sRUFBRSxhQUFhLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLFlBQVksRUFBRSxXQUFXLENBQUMsRUFBRSxDQUFDO0FBQy9GLENBQUMsQ0FBQztBQUVGLHNFQUFzRTtBQUN0RSxNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBRyw4QkFBOEIsQ0FBQztBQUUxRTs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSxnQ0FBZ0MsR0FBRyxDQUFDLEdBQXNCLEVBQUUsYUFBcUIsRUFBVSxFQUFFO0lBQ3hHLE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxhQUFhLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBQ3ZGLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLHlDQUF5QyxDQUFDLENBQUM7SUFDbkcsT0FBTyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsY0FBYyxDQUFDLENBQUM7QUFDckQsQ0FBQyxDQUFDIn0=
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { ITypedRequest } from '@api.global/typedrequest-interfaces';
|
|
2
2
|
import type { IAuthSwitchBinding } from './authority-contract.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Backend-only binding operation, served on the MANAGEMENT socket. Not a misplacement -- the rule.
|
|
5
|
+
*
|
|
6
|
+
* The other two requests in this file are answered on the runtime socket, whose directory a container may
|
|
7
|
+
* be given. This one is not, because it mints the capability those two spend: minting is the act that
|
|
8
|
+
* authorizes a runtime, so it belongs to a caller already trusted with account management. A container
|
|
9
|
+
* holding the runtime directory can therefore use the binding it was handed, and can never create one --
|
|
10
|
+
* not for itself, and not for another account.
|
|
11
|
+
*
|
|
12
|
+
* The capability must never enter a browser-facing response.
|
|
13
|
+
*/
|
|
4
14
|
export interface IReq_AuthSwitchBindAccount extends ITypedRequest {
|
|
5
15
|
method: 'authswitch.authority.bind';
|
|
6
16
|
request: {
|
|
@@ -88,6 +88,15 @@ export declare class AuthSwitchAuthorityBroker {
|
|
|
88
88
|
resolveAccess(bindingId: string, capability: string, minValidityMs: number, rejectedGrantGeneration?: number): Promise<IAuthSwitchResolvedAccess>;
|
|
89
89
|
/** Backend-only access for usage; it never creates or bypasses a runtime binding. */
|
|
90
90
|
resolveUsageAccess(context: IAuthSwitchUsageContext, rejectedGrantGeneration: number | undefined, minValidityMs: number): Promise<IAuthSwitchResolvedAccess>;
|
|
91
|
+
/**
|
|
92
|
+
* The shared managed-access loop, whose refusals are deliberately still unmarked faults.
|
|
93
|
+
*
|
|
94
|
+
* Its two callers repair differently: a runtime holding a binding binds again, while the usage reader has
|
|
95
|
+
* no binding at all and collapses everything into one usage problem. Naming a code here would either say
|
|
96
|
+
* "binding" to a caller that has none, or invent a second meaning for one that does, so the decision waits
|
|
97
|
+
* for the slice that gives the usage answer its own vocabulary. What a bound runtime can act on is decided
|
|
98
|
+
* before this point, in the view `resolveAccess` supplies.
|
|
99
|
+
*/
|
|
91
100
|
private resolveManagedAccess;
|
|
92
101
|
private refreshAccount;
|
|
93
102
|
private performRefresh;
|