@opendatalabs/vana-sdk 3.0.1 → 3.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/dist/__tests__/interop-personal-server.test.d.ts +1 -0
- package/dist/auth/oauth-client.cjs +250 -0
- package/dist/auth/oauth-client.cjs.map +1 -0
- package/dist/auth/oauth-client.d.ts +90 -0
- package/dist/auth/oauth-client.js +228 -0
- package/dist/auth/oauth-client.js.map +1 -0
- package/dist/auth/oauth-client.test.d.ts +1 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +224 -1
- package/dist/index.browser.js.map +3 -3
- package/dist/index.node.cjs +225 -1
- package/dist/index.node.cjs.map +3 -3
- package/dist/index.node.d.ts +1 -0
- package/dist/index.node.js +224 -1
- package/dist/index.node.js.map +3 -3
- package/dist/storage/index.cjs.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/providers/vana-storage.cjs +1 -1
- package/dist/storage/providers/vana-storage.cjs.map +1 -1
- package/dist/storage/providers/vana-storage.d.ts +2 -2
- package/dist/storage/providers/vana-storage.js +1 -1
- package/dist/storage/providers/vana-storage.js.map +1 -1
- package/package.json +2 -1
package/dist/index.node.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export { buildWeb3SignedHeader, computeBodyHash, type Web3SignedSignFn, } from "
|
|
|
34
34
|
export { MissingAuthError, InvalidSignatureError, ExpiredTokenError, } from "./auth/errors";
|
|
35
35
|
export { generatePkceVerifier, computePkceChallenge, verifyPkceChallenge, assertValidPkceVerifier, PKCE_VERIFIER_PATTERN, PKCE_CHALLENGE_PATTERN, } from "./auth/pkce";
|
|
36
36
|
export { InMemoryTokenStore, type TokenStore, type TokenRecord, } from "./auth/token-store";
|
|
37
|
+
export { OAuthClient, type OAuthClientConfig, type AuthorizationUrlResult, } from "./auth/oauth-client";
|
|
37
38
|
export { fileRegistrationDomain, grantRegistrationDomain, grantRevocationDomain, serverRegistrationDomain, builderRegistrationDomain, FILE_REGISTRATION_TYPES, GRANT_REGISTRATION_TYPES, GRANT_REVOCATION_TYPES, SERVER_REGISTRATION_TYPES, BUILDER_REGISTRATION_TYPES, type DataPortabilityContracts, type DataPortabilityGatewayConfig, type FileRegistrationMessage, type GrantRegistrationMessage, type GrantRevocationMessage, type ServerRegistrationMessage, type BuilderRegistrationMessage, } from "./protocol/eip712";
|
|
38
39
|
export { isDataPortabilityGatewayConfig, parseGrantRegistrationPayload, verifyGrantRegistration, type DataPortabilityGrantPayload, type VerifyGrantRegistrationInput, type VerifyGrantRegistrationResult, } from "./protocol/grants";
|
|
39
40
|
export { ScopeSchema, parseScope, scopeToPathSegments, scopeMatchesPattern, scopeCoveredByGrant, type Scope, type ParsedScope, } from "./protocol/scopes";
|
package/dist/index.node.js
CHANGED
|
@@ -29015,7 +29015,7 @@ async function buildWeb3SignedHeader(params) {
|
|
|
29015
29015
|
}
|
|
29016
29016
|
|
|
29017
29017
|
// src/storage/providers/vana-storage.ts
|
|
29018
|
-
var DEFAULT_ENDPOINT = "https://storage.vana.
|
|
29018
|
+
var DEFAULT_ENDPOINT = "https://storage.vana.org";
|
|
29019
29019
|
var BLOB_PATH_PREFIX = "/v1/blobs";
|
|
29020
29020
|
var DEFAULT_TOKEN_TTL_SECONDS = 300;
|
|
29021
29021
|
var VanaStorage = class {
|
|
@@ -32284,6 +32284,228 @@ var InMemoryTokenStore = class {
|
|
|
32284
32284
|
}
|
|
32285
32285
|
};
|
|
32286
32286
|
|
|
32287
|
+
// src/auth/oauth-client.ts
|
|
32288
|
+
var VERIFIER_TTL_SECONDS = 600;
|
|
32289
|
+
var RESERVED_AUTHORIZE_PARAMS = /* @__PURE__ */ new Set([
|
|
32290
|
+
"response_type",
|
|
32291
|
+
"client_id",
|
|
32292
|
+
"redirect_uri",
|
|
32293
|
+
"scope",
|
|
32294
|
+
"state",
|
|
32295
|
+
"code_challenge",
|
|
32296
|
+
"code_challenge_method"
|
|
32297
|
+
]);
|
|
32298
|
+
var OAuthClient = class {
|
|
32299
|
+
#config;
|
|
32300
|
+
constructor(config) {
|
|
32301
|
+
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
32302
|
+
if (typeof fetchImpl !== "function") {
|
|
32303
|
+
throw new TypeError(
|
|
32304
|
+
"OAuthClient requires a global `fetch` or an explicit `fetchImpl`"
|
|
32305
|
+
);
|
|
32306
|
+
}
|
|
32307
|
+
this.#config = {
|
|
32308
|
+
authorizationEndpoint: config.authorizationEndpoint,
|
|
32309
|
+
tokenEndpoint: config.tokenEndpoint,
|
|
32310
|
+
clientId: config.clientId,
|
|
32311
|
+
redirectUri: config.redirectUri,
|
|
32312
|
+
scope: config.scope,
|
|
32313
|
+
tokenStore: config.tokenStore ?? new InMemoryTokenStore(),
|
|
32314
|
+
fetchImpl,
|
|
32315
|
+
generateState: config.generateState ?? defaultGenerateState
|
|
32316
|
+
};
|
|
32317
|
+
}
|
|
32318
|
+
/** Build the authorize URL and persist the PKCE verifier keyed by `state`. */
|
|
32319
|
+
async buildAuthorizationUrl(opts = {}) {
|
|
32320
|
+
const state = opts.state ?? this.#config.generateState();
|
|
32321
|
+
const scope = opts.scope ?? this.#config.scope;
|
|
32322
|
+
const verifier = generatePkceVerifier();
|
|
32323
|
+
const challenge = await computePkceChallenge(verifier);
|
|
32324
|
+
await this.#config.tokenStore.set(this.#verifierKey(state), {
|
|
32325
|
+
token: verifier,
|
|
32326
|
+
expiresAt: Math.floor(Date.now() / 1e3) + VERIFIER_TTL_SECONDS
|
|
32327
|
+
});
|
|
32328
|
+
const params = new URLSearchParams();
|
|
32329
|
+
params.set("response_type", "code");
|
|
32330
|
+
params.set("client_id", this.#config.clientId);
|
|
32331
|
+
params.set("redirect_uri", this.#config.redirectUri);
|
|
32332
|
+
if (scope !== void 0 && scope.length > 0) {
|
|
32333
|
+
params.set("scope", scope);
|
|
32334
|
+
}
|
|
32335
|
+
params.set("state", state);
|
|
32336
|
+
params.set("code_challenge", challenge);
|
|
32337
|
+
params.set("code_challenge_method", "S256");
|
|
32338
|
+
if (opts.extraParams !== void 0) {
|
|
32339
|
+
for (const k of Object.keys(opts.extraParams)) {
|
|
32340
|
+
if (RESERVED_AUTHORIZE_PARAMS.has(k)) {
|
|
32341
|
+
throw new Error(
|
|
32342
|
+
`extraParams may not override the reserved OAuth/PKCE parameter "${k}"`
|
|
32343
|
+
);
|
|
32344
|
+
}
|
|
32345
|
+
}
|
|
32346
|
+
for (const [k, v] of Object.entries(opts.extraParams)) {
|
|
32347
|
+
params.set(k, v);
|
|
32348
|
+
}
|
|
32349
|
+
}
|
|
32350
|
+
const sep = this.#config.authorizationEndpoint.includes("?") ? "&" : "?";
|
|
32351
|
+
const url = `${this.#config.authorizationEndpoint}${sep}${params.toString()}`;
|
|
32352
|
+
return { url, state };
|
|
32353
|
+
}
|
|
32354
|
+
/**
|
|
32355
|
+
* Handle the redirect-callback URL. Validates `state`, retrieves the saved
|
|
32356
|
+
* verifier, exchanges the authorization code + verifier for tokens, and
|
|
32357
|
+
* persists them. Returns the access {@link TokenRecord}.
|
|
32358
|
+
*/
|
|
32359
|
+
async handleCallback(callbackUrl) {
|
|
32360
|
+
const parsed = new URL(callbackUrl);
|
|
32361
|
+
const params = parsed.searchParams;
|
|
32362
|
+
const errorCode = params.get("error");
|
|
32363
|
+
if (errorCode !== null) {
|
|
32364
|
+
throw new Error(
|
|
32365
|
+
formatOAuthError({
|
|
32366
|
+
error: errorCode,
|
|
32367
|
+
error_description: params.get("error_description") ?? void 0
|
|
32368
|
+
})
|
|
32369
|
+
);
|
|
32370
|
+
}
|
|
32371
|
+
const code = params.get("code");
|
|
32372
|
+
const state = params.get("state");
|
|
32373
|
+
if (code === null || state === null) {
|
|
32374
|
+
throw new Error("OAuth callback is missing `code` or `state`");
|
|
32375
|
+
}
|
|
32376
|
+
const verifierRecord = await this.#config.tokenStore.get(
|
|
32377
|
+
this.#verifierKey(state)
|
|
32378
|
+
);
|
|
32379
|
+
if (verifierRecord === null) {
|
|
32380
|
+
throw new Error(
|
|
32381
|
+
"OAuth callback state does not match any in-flight verifier (possible CSRF or expired flow)"
|
|
32382
|
+
);
|
|
32383
|
+
}
|
|
32384
|
+
const body = new URLSearchParams();
|
|
32385
|
+
body.set("grant_type", "authorization_code");
|
|
32386
|
+
body.set("code", code);
|
|
32387
|
+
body.set("redirect_uri", this.#config.redirectUri);
|
|
32388
|
+
body.set("client_id", this.#config.clientId);
|
|
32389
|
+
body.set("code_verifier", verifierRecord.token);
|
|
32390
|
+
let tokens;
|
|
32391
|
+
try {
|
|
32392
|
+
tokens = await this.#tokenRequest(body);
|
|
32393
|
+
} finally {
|
|
32394
|
+
await this.#config.tokenStore.delete(this.#verifierKey(state));
|
|
32395
|
+
}
|
|
32396
|
+
return this.#persistTokens(tokens);
|
|
32397
|
+
}
|
|
32398
|
+
/**
|
|
32399
|
+
* Exchange a stored refresh token for a fresh access token. Throws if no
|
|
32400
|
+
* refresh token is available.
|
|
32401
|
+
*/
|
|
32402
|
+
async refresh() {
|
|
32403
|
+
const refreshRecord = await this.#config.tokenStore.get(this.#refreshKey());
|
|
32404
|
+
if (refreshRecord === null) {
|
|
32405
|
+
throw new Error("OAuth refresh failed: no refresh token stored");
|
|
32406
|
+
}
|
|
32407
|
+
const body = new URLSearchParams();
|
|
32408
|
+
body.set("grant_type", "refresh_token");
|
|
32409
|
+
body.set("refresh_token", refreshRecord.token);
|
|
32410
|
+
body.set("client_id", this.#config.clientId);
|
|
32411
|
+
const tokens = await this.#tokenRequest(body);
|
|
32412
|
+
return this.#persistTokens(tokens, refreshRecord.token);
|
|
32413
|
+
}
|
|
32414
|
+
/**
|
|
32415
|
+
* Get the current access token if valid (refreshing first if expired and a
|
|
32416
|
+
* refresh token is available). Returns `null` when no usable token exists.
|
|
32417
|
+
*/
|
|
32418
|
+
async getAccessToken() {
|
|
32419
|
+
const stored = await this.#config.tokenStore.get(this.#accessKey());
|
|
32420
|
+
if (stored !== null) return stored.token;
|
|
32421
|
+
const refresh = await this.#config.tokenStore.get(this.#refreshKey());
|
|
32422
|
+
if (refresh === null) return null;
|
|
32423
|
+
try {
|
|
32424
|
+
const refreshed = await this.refresh();
|
|
32425
|
+
return refreshed.token;
|
|
32426
|
+
} catch {
|
|
32427
|
+
return null;
|
|
32428
|
+
}
|
|
32429
|
+
}
|
|
32430
|
+
/** Forget tokens (logout). Does NOT call any remote revocation endpoint. */
|
|
32431
|
+
async signOut() {
|
|
32432
|
+
await this.#config.tokenStore.delete(this.#accessKey());
|
|
32433
|
+
await this.#config.tokenStore.delete(this.#refreshKey());
|
|
32434
|
+
}
|
|
32435
|
+
#accessKey() {
|
|
32436
|
+
return `oauth:tokens:${this.#config.clientId}`;
|
|
32437
|
+
}
|
|
32438
|
+
#refreshKey() {
|
|
32439
|
+
return `oauth:refresh:${this.#config.clientId}`;
|
|
32440
|
+
}
|
|
32441
|
+
#verifierKey(state) {
|
|
32442
|
+
return `oauth:verifier:${state}`;
|
|
32443
|
+
}
|
|
32444
|
+
async #tokenRequest(body) {
|
|
32445
|
+
const response = await this.#config.fetchImpl(this.#config.tokenEndpoint, {
|
|
32446
|
+
method: "POST",
|
|
32447
|
+
headers: {
|
|
32448
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
32449
|
+
Accept: "application/json"
|
|
32450
|
+
},
|
|
32451
|
+
body: body.toString()
|
|
32452
|
+
});
|
|
32453
|
+
const text = await response.text();
|
|
32454
|
+
const parsed = parseJsonBody(text);
|
|
32455
|
+
if (!response.ok) {
|
|
32456
|
+
throw new Error(formatOAuthError(parsed ?? {}, response.status));
|
|
32457
|
+
}
|
|
32458
|
+
if (parsed === null || typeof parsed !== "object" || typeof parsed.access_token !== "string") {
|
|
32459
|
+
throw new Error(
|
|
32460
|
+
"OAuth token endpoint returned a response without an `access_token` string"
|
|
32461
|
+
);
|
|
32462
|
+
}
|
|
32463
|
+
return parsed;
|
|
32464
|
+
}
|
|
32465
|
+
async #persistTokens(tokens, previousRefreshToken) {
|
|
32466
|
+
const record = { token: tokens.access_token };
|
|
32467
|
+
if (typeof tokens.expires_in === "number" && tokens.expires_in > 0) {
|
|
32468
|
+
record.expiresAt = Math.floor(Date.now() / 1e3) + tokens.expires_in;
|
|
32469
|
+
}
|
|
32470
|
+
await this.#config.tokenStore.set(this.#accessKey(), record);
|
|
32471
|
+
const newRefresh = tokens.refresh_token ?? previousRefreshToken;
|
|
32472
|
+
if (newRefresh !== void 0) {
|
|
32473
|
+
await this.#config.tokenStore.set(this.#refreshKey(), {
|
|
32474
|
+
token: newRefresh
|
|
32475
|
+
});
|
|
32476
|
+
}
|
|
32477
|
+
return record;
|
|
32478
|
+
}
|
|
32479
|
+
};
|
|
32480
|
+
function defaultGenerateState() {
|
|
32481
|
+
const bytes = new Uint8Array(24);
|
|
32482
|
+
crypto.getRandomValues(bytes);
|
|
32483
|
+
let binary = "";
|
|
32484
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
32485
|
+
binary += String.fromCharCode(bytes[i]);
|
|
32486
|
+
}
|
|
32487
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
32488
|
+
}
|
|
32489
|
+
function parseJsonBody(text) {
|
|
32490
|
+
if (text.length === 0) return null;
|
|
32491
|
+
try {
|
|
32492
|
+
return JSON.parse(text);
|
|
32493
|
+
} catch {
|
|
32494
|
+
return null;
|
|
32495
|
+
}
|
|
32496
|
+
}
|
|
32497
|
+
function formatOAuthError(body, status) {
|
|
32498
|
+
const parts = ["OAuth token request failed"];
|
|
32499
|
+
if (status !== void 0) parts.push(`(HTTP ${String(status)})`);
|
|
32500
|
+
if (body.error !== void 0 && body.error.length > 0) {
|
|
32501
|
+
parts.push(`: ${body.error}`);
|
|
32502
|
+
if (body.error_description !== void 0 && body.error_description.length > 0) {
|
|
32503
|
+
parts.push(`- ${body.error_description}`);
|
|
32504
|
+
}
|
|
32505
|
+
}
|
|
32506
|
+
return parts.join(" ").replace(" : ", ": ").replace(" - ", " - ");
|
|
32507
|
+
}
|
|
32508
|
+
|
|
32287
32509
|
// src/protocol/eip712.ts
|
|
32288
32510
|
var DOMAIN_NAME = "Vana Data Portability";
|
|
32289
32511
|
var DOMAIN_VERSION = "1";
|
|
@@ -32837,6 +33059,7 @@ export {
|
|
|
32837
33059
|
NodeECIESUint8Provider as NodeECIESProvider,
|
|
32838
33060
|
NodePlatformAdapter,
|
|
32839
33061
|
NonceError,
|
|
33062
|
+
OAuthClient,
|
|
32840
33063
|
PKCE_CHALLENGE_PATTERN,
|
|
32841
33064
|
PKCE_VERIFIER_PATTERN,
|
|
32842
33065
|
PSError,
|