@crowdedkingdoms/crowdyjs 7.1.1 → 8.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.
@@ -60,6 +60,30 @@ export declare class BrowserSessionPkceStore implements PkceStore {
60
60
  set(state: string, verifier: string): void;
61
61
  remove(state: string): void;
62
62
  }
63
+ export interface PortalConsentState {
64
+ appId: string;
65
+ appName: string | null;
66
+ /** True for first-party/trusted apps (consent always skipped). */
67
+ trusted: boolean;
68
+ alreadyGranted: boolean;
69
+ /** True if the Overworld must show a consent screen before minting a code. */
70
+ consentRequired: boolean;
71
+ }
72
+ export interface AppAuthorizationGrant {
73
+ grantId: string;
74
+ appId: string;
75
+ appName: string | null;
76
+ scopes: string[];
77
+ status: string;
78
+ grantedAt: string;
79
+ revokedAt: string | null;
80
+ }
81
+ /** Thrown by {@link PortalAPI.handleAuthorizeRequest} when the user must consent. */
82
+ export declare class PortalConsentRequiredError extends Error {
83
+ readonly appId: string;
84
+ readonly appName: string | null;
85
+ constructor(appId: string, appName: string | null);
86
+ }
63
87
  export interface BeginEntryParams {
64
88
  /** Target app id (decimal string). */
65
89
  appId: string;
@@ -105,6 +129,21 @@ export declare class PortalAPI {
105
129
  * through the Overworld. Requires the current app token on this session.
106
130
  */
107
131
  refresh(): Promise<AppTokenResponse>;
132
+ /** Whether portaling into an app needs a consent prompt (Overworld side). */
133
+ getConsent(appId: string): Promise<PortalConsentState>;
134
+ /** Record the user's consent for an app (call from the consent screen). */
135
+ authorizeApp(appId: string, scopes?: string[]): Promise<AppAuthorizationGrant>;
136
+ /** Revoke a prior authorization; also revokes the user's live tokens for it. */
137
+ revokeAppAuthorization(appId: string): Promise<boolean>;
138
+ /** The user's active app authorizations ("connected apps"). */
139
+ myAuthorizedApps(): Promise<AppAuthorizationGrant[]>;
140
+ /** Register/update an app's portal client settings (requires manage_apps). */
141
+ setAppClientSettings(input: {
142
+ appId: string;
143
+ redirectUris?: string[];
144
+ clientType?: string;
145
+ launchUrl?: string;
146
+ }): Promise<PortalConsentState>;
108
147
  /**
109
148
  * Destination-game side, step 1: generate a PKCE pair, persist the verifier,
110
149
  * and return the Overworld authorize URL to navigate to. The caller does
@@ -116,7 +155,10 @@ export declare class PortalAPI {
116
155
  * game's params from the URL, mints a code with the session token, and returns
117
156
  * the URL to redirect the player back to (carrying `code` + `state`).
118
157
  */
119
- handleAuthorizeRequest(search?: string): Promise<string>;
158
+ handleAuthorizeRequest(search?: string, options?: {
159
+ grantConsent?: boolean;
160
+ scopes?: string[];
161
+ }): Promise<string>;
120
162
  /**
121
163
  * Destination-game side, step 3: read `code` + `state` from the callback URL,
122
164
  * load the stored verifier, exchange for an app token, and store it. Returns
@@ -1 +1 @@
1
- {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/domains/portal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/domains/portal.ts"],"names":[],"mappings":"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"}
@@ -48,6 +48,20 @@ const MintAppTokenDocument = parse(`mutation MintAppToken($input: MintAppTokenIn
48
48
  const CreatePortalAuthorizationCodeDocument = parse(`mutation CreatePortalAuthorizationCode($input: CreatePortalAuthorizationCodeInput!) { createPortalAuthorizationCode(input: $input) { code redirectUri expiresAt } }`);
49
49
  const ExchangePortalCodeDocument = parse(`mutation ExchangePortalCode($input: ExchangePortalCodeInput!) { exchangePortalCode(input: $input) { ${APP_TOKEN_FIELDS} } }`);
50
50
  const RefreshAppTokenDocument = parse(`mutation RefreshAppToken { refreshAppToken { ${APP_TOKEN_FIELDS} } }`);
51
+ /** Thrown by {@link PortalAPI.handleAuthorizeRequest} when the user must consent. */
52
+ export class PortalConsentRequiredError extends Error {
53
+ constructor(appId, appName) {
54
+ super(`Consent required for app ${appId}`);
55
+ this.appId = appId;
56
+ this.appName = appName;
57
+ this.name = 'PortalConsentRequiredError';
58
+ }
59
+ }
60
+ const PortalConsentDocument = parse(`query PortalConsent($appId: BigInt!) { portalConsent(appId: $appId) { appId appName trusted alreadyGranted consentRequired } }`);
61
+ const AuthorizeAppDocument = parse(`mutation AuthorizeApp($input: AuthorizeAppInput!) { authorizeApp(input: $input) { grantId appId status scopes } }`);
62
+ const RevokeAppAuthorizationDocument = parse(`mutation RevokeAppAuthorization($appId: BigInt!) { revokeAppAuthorization(appId: $appId) }`);
63
+ const MyAuthorizedAppsDocument = parse(`query MyAuthorizedApps { myAuthorizedApps { grantId appId appName scopes status grantedAt revokedAt } }`);
64
+ const SetAppClientSettingsDocument = parse(`mutation SetAppClientSettings($input: SetAppClientSettingsInput!) { setAppClientSettings(input: $input) { appId appName trusted consentRequired } }`);
51
65
  export class PortalAPI {
52
66
  constructor(management, session, pkceStore = new BrowserSessionPkceStore()) {
53
67
  this.management = management;
@@ -97,6 +111,38 @@ export class PortalAPI {
97
111
  this.session.setToken(data.refreshAppToken.token);
98
112
  return data.refreshAppToken;
99
113
  }
114
+ // ----- Consent + connected apps ------------------------------------------
115
+ /** Whether portaling into an app needs a consent prompt (Overworld side). */
116
+ async getConsent(appId) {
117
+ const data = await this.management.request(PortalConsentDocument, { appId });
118
+ return data.portalConsent;
119
+ }
120
+ /** Record the user's consent for an app (call from the consent screen). */
121
+ async authorizeApp(appId, scopes) {
122
+ const data = await this.management.request(AuthorizeAppDocument, {
123
+ input: { appId, scopes },
124
+ });
125
+ return data.authorizeApp;
126
+ }
127
+ /** Revoke a prior authorization; also revokes the user's live tokens for it. */
128
+ async revokeAppAuthorization(appId) {
129
+ const data = await this.management.request(RevokeAppAuthorizationDocument, {
130
+ appId,
131
+ });
132
+ return data.revokeAppAuthorization;
133
+ }
134
+ /** The user's active app authorizations ("connected apps"). */
135
+ async myAuthorizedApps() {
136
+ const data = await this.management.request(MyAuthorizedAppsDocument);
137
+ return data.myAuthorizedApps;
138
+ }
139
+ /** Register/update an app's portal client settings (requires manage_apps). */
140
+ async setAppClientSettings(input) {
141
+ const data = await this.management.request(SetAppClientSettingsDocument, {
142
+ input,
143
+ });
144
+ return data.setAppClientSettings;
145
+ }
100
146
  // ----- Browser PKCE redirect helpers -------------------------------------
101
147
  /**
102
148
  * Destination-game side, step 1: generate a PKCE pair, persist the verifier,
@@ -120,7 +166,7 @@ export class PortalAPI {
120
166
  * game's params from the URL, mints a code with the session token, and returns
121
167
  * the URL to redirect the player back to (carrying `code` + `state`).
122
168
  */
123
- async handleAuthorizeRequest(search) {
169
+ async handleAuthorizeRequest(search, options) {
124
170
  const params = new URLSearchParams(search ?? defaultSearch());
125
171
  const appId = params.get('app_id');
126
172
  const codeChallenge = params.get('code_challenge');
@@ -130,6 +176,18 @@ export class PortalAPI {
130
176
  if (!appId || !codeChallenge || !redirectUri) {
131
177
  throw new Error('authorize request missing app_id, code_challenge, or redirect_uri');
132
178
  }
179
+ // Consent gate: trusted apps + already-granted apps proceed silently. For an
180
+ // untrusted, not-yet-granted app, either record consent (when the user
181
+ // approved on the consent screen) or signal the caller to show one.
182
+ const consent = await this.getConsent(appId);
183
+ if (consent.consentRequired) {
184
+ if (options?.grantConsent) {
185
+ await this.authorizeApp(appId, options.scopes);
186
+ }
187
+ else {
188
+ throw new PortalConsentRequiredError(appId, consent.appName);
189
+ }
190
+ }
133
191
  const result = await this.createAuthorizationCode({
134
192
  appId,
135
193
  codeChallenge,