@crowdedkingdoms/crowdyjs 15.5.0-test.1 → 15.6.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +65 -0
- package/README.md +63 -44
- package/dist/default-origin.d.ts +6 -6
- package/dist/default-origin.d.ts.map +1 -1
- package/dist/default-origin.js +6 -6
- package/dist/domains/auth.d.ts +28 -6
- package/dist/domains/auth.d.ts.map +1 -1
- package/dist/domains/auth.js +18 -6
- package/dist/domains/portal.d.ts +97 -13
- package/dist/domains/portal.d.ts.map +1 -1
- package/dist/domains/portal.js +128 -11
- package/dist/generated/graphql.d.ts +17 -13
- package/dist/generated/graphql.d.ts.map +1 -1
- package/dist/generated/graphql.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/kit/npcs.d.ts.map +1 -1
- package/dist/kit/social.d.ts.map +1 -1
- package/package.json +1 -1
package/MIGRATION.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
+
# CrowdyJS v15.6 — a browser game signs in through Studio, not through `auth.*`
|
|
2
|
+
|
|
3
|
+
**Nothing removed, two methods added, and a platform rule that decides which
|
|
4
|
+
of the two sign-in paths your code may take.** ck-api `v1.88.0` (2026-09-08).
|
|
5
|
+
|
|
6
|
+
## Direct sign-in is first-party only
|
|
7
|
+
|
|
8
|
+
`auth.login`, `auth.register`, `auth.requestLoginLink` / `completeLoginLink`,
|
|
9
|
+
`auth.socialLoginStart` / `socialLoginComplete`, `auth.checkAuthMethod`,
|
|
10
|
+
`auth.requestPasswordReset` / `resetPassword` are now served only to:
|
|
11
|
+
|
|
12
|
+
- **first-party browser origins** — Studio and the crowdy.games host; and
|
|
13
|
+
- **non-browser callers** — anything that sends no `Origin` header (Node, a
|
|
14
|
+
CLI, CrowdyCPP, tests).
|
|
15
|
+
|
|
16
|
+
From a browser page on **any other origin** (every customer's game) the API
|
|
17
|
+
refuses with `extensions.code` **`HOSTED_SIGN_IN_REQUIRED`** (403).
|
|
18
|
+
`isHostedSignInRequiredError(e)` recognises it. The reason is the player's
|
|
19
|
+
password: a form on a customer's domain that collects it is indistinguishable,
|
|
20
|
+
to the platform and to the player, from a phishing page.
|
|
21
|
+
|
|
22
|
+
## What to call instead: `portal.signIn` and `portal.handleSignInCallback`
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// boot: finish a sign-in we are returning from (no-op without ?code=)
|
|
26
|
+
const entered = await client.portal.handleSignInCallback();
|
|
27
|
+
|
|
28
|
+
// "Sign in with Crowded Kingdoms" button
|
|
29
|
+
await client.portal.signIn({ appId, redirectUri: `${location.origin}/auth/callback` });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`signIn` sends the player to Studio's hosted `/authorize` with a PKCE
|
|
33
|
+
challenge; the player signs in there (and consents, if your app is not
|
|
34
|
+
trusted); Studio redirects back to your `redirectUri` with a one-time code;
|
|
35
|
+
`handleSignInCallback` exchanges it for an **app-scoped token** and stores it
|
|
36
|
+
on the client. You never held a session token, and you never needed one.
|
|
37
|
+
|
|
38
|
+
- The hosted page is derived from the API host you configured
|
|
39
|
+
(`ck.<tier>.crowdedkingdoms.com` -> `studio.<tier>.crowdedkingdoms.com/authorize`,
|
|
40
|
+
`localhost:3000` -> `localhost:3001`). Pass `authorizeUrl` to override;
|
|
41
|
+
`defaultHostedSignInUrl(endpoint)` is the derivation.
|
|
42
|
+
- Your `redirectUri`'s **origin** must be one of the app's registered redirect
|
|
43
|
+
URIs (Studio > Apps > client settings). That same entry is what puts your
|
|
44
|
+
origin on the API's CORS allow-list, live, without a restart.
|
|
45
|
+
- `beginEntry` / `completeEntry` are the same two steps without the defaults
|
|
46
|
+
and are unchanged. `handleAuthorizeRequest` is what Studio's page calls.
|
|
47
|
+
|
|
48
|
+
## If you are first-party or not in a browser, nothing changes
|
|
49
|
+
|
|
50
|
+
Studio, the Overworld lobby, CrowdyCPP, load tools and scripts keep calling
|
|
51
|
+
`auth.login` / `register` and then `portal.mintAppToken(appId)`.
|
|
52
|
+
|
|
53
|
+
## Also in this release (server side, no SDK change)
|
|
54
|
+
|
|
55
|
+
- `resetPassword` now revokes **every** session of the account; `changePassword`
|
|
56
|
+
every session but the calling one. A client that kept a second tab signed in
|
|
57
|
+
will find it signed out after either.
|
|
58
|
+
- Ten failed `login` attempts for one address in fifteen minutes answer
|
|
59
|
+
`RATE_LIMITED`; `register`, `checkAuthMethod`, the reset and resend mutations
|
|
60
|
+
and `requestLoginLink` are rate-limited per address and per client too.
|
|
61
|
+
- GraphQL introspection is off on every tier; the SDL is published at
|
|
62
|
+
docs.crowdedkingdoms.com and shipped in this package.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
1
66
|
# CrowdyJS v15.4 — nothing runs for an app with no player in it
|
|
2
67
|
|
|
3
68
|
**One removal, one addition, and a change in what the platform does that no SDK
|
package/README.md
CHANGED
|
@@ -49,31 +49,33 @@ const client = createCrowdyClient({
|
|
|
49
49
|
},
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
//
|
|
52
|
+
// A browser game on its own domain signs players in through Crowded Kingdoms'
|
|
53
|
+
// HOSTED sign-in: the player types their password into Studio, never into your
|
|
54
|
+
// page, and your client comes back holding a token confined to your app.
|
|
53
55
|
await client.session.restore();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
await client.auth.requestLoginLink({ email: 'player@example.com', redirectUri });
|
|
60
|
-
await client.auth.completeLoginLink(tokenFromLink);
|
|
61
|
-
// Or social/OIDC: socialLoginStart('google', redirectUri) -> socialLoginComplete({ provider, code, state })
|
|
56
|
+
const entered = await client.portal.handleSignInCallback(); // no-op without ?code=
|
|
57
|
+
if (!entered && !client.session.getToken()) {
|
|
58
|
+
// On your "Sign in with Crowded Kingdoms" button:
|
|
59
|
+
await client.portal.signIn({ appId, redirectUri: `${location.origin}/auth/callback` });
|
|
60
|
+
return; // the browser is navigating to Studio
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
//
|
|
65
|
-
// Identity reads run on it:
|
|
63
|
+
// The client now holds an app-scoped token: gameplay reads and the UDP proxy run on it.
|
|
66
64
|
const me = await client.users.me();
|
|
67
65
|
console.log(me.email);
|
|
68
66
|
```
|
|
69
67
|
|
|
68
|
+
Your game's origin must be registered under the app's **redirect URIs**
|
|
69
|
+
(Studio > Apps > client settings); that one entry admits it to CORS and to the
|
|
70
|
+
hosted sign-in return. Direct sign-in (`client.auth.login` / `register` / magic
|
|
71
|
+
link / social) is served only to first-party pages (Studio, crowdy.games) and to
|
|
72
|
+
non-browser callers (Node, CLI, tests); from any other browser origin the API
|
|
73
|
+
answers `HOSTED_SIGN_IN_REQUIRED` (`isHostedSignInRequiredError`). See
|
|
74
|
+
[Authentication](#authentication-session-token-vs-app-scoped-tokens).
|
|
75
|
+
|
|
70
76
|
There is one endpoint. `managementUrl` was removed in v14 — see
|
|
71
77
|
[MIGRATION.md](MIGRATION.md).
|
|
72
78
|
|
|
73
|
-
**Gameplay needs an app-scoped token, not the session token.** Mint one per
|
|
74
|
-
app and drive the world/UDP surface from a per-game client — see
|
|
75
|
-
[Authentication](#authentication-session-token-vs-app-scoped-tokens).
|
|
76
|
-
|
|
77
79
|
## Authentication: session token vs app-scoped tokens
|
|
78
80
|
|
|
79
81
|
Sign-in returns an **identity session token** good for account,
|
|
@@ -81,23 +83,57 @@ studio admin and token minting, and **rejected for gameplay**. Each game is
|
|
|
81
83
|
entered with a short-lived **app-scoped token** confined to that one app, so a
|
|
82
84
|
game stack never receives the player's full session.
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
**Which flow you use depends on where your code runs** (ck-api v1.88.0,
|
|
87
|
+
2026-09-08):
|
|
88
|
+
|
|
89
|
+
| your code runs... | sign in with | then |
|
|
90
|
+
|-----------------------------------------------------|-------------------------------------------|---------------------------------------|
|
|
91
|
+
| in a browser, on your own domain (every customer game) | `portal.signIn` -> Studio -> `portal.handleSignInCallback` | the client already holds the app token |
|
|
92
|
+
| in a browser, on a first-party host (Studio, crowdy.games) | `auth.login` / magic link / social | `portal.mintAppToken(appId)` |
|
|
93
|
+
| outside a browser (Node, CLI, CrowdyCPP, tests) | `auth.login` / `auth.register` | `portal.mintAppToken(appId)` |
|
|
94
|
+
|
|
95
|
+
The first row is the only one a game on its own domain can take: the direct
|
|
96
|
+
sign-in mutations are refused from a non-first-party browser origin with
|
|
97
|
+
`HOSTED_SIGN_IN_REQUIRED`. The reason is the player's password: a form on a
|
|
98
|
+
customer's domain that collects it is indistinguishable, to the platform and to
|
|
99
|
+
the player, from a phishing page.
|
|
100
|
+
|
|
101
|
+
**Hosted sign-in** (browser game, own domain) is OAuth2 Authorization Code +
|
|
102
|
+
PKCE; the verifier never leaves your origin and your page never sees a
|
|
103
|
+
credential:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const game = createCrowdyClient({ httpUrl: apiUrl, wsUrl,
|
|
107
|
+
tokenStore: new BrowserLocalStorageTokenStore('crowdyjs:app:' + appId) });
|
|
108
|
+
|
|
109
|
+
// Boot: finish a sign-in we are returning from (no-op without ?code=).
|
|
110
|
+
const entered = await game.portal.handleSignInCallback();
|
|
111
|
+
|
|
112
|
+
// "Sign in with Crowded Kingdoms" button: go to Studio's hosted page and come back.
|
|
113
|
+
await game.portal.signIn({ appId, redirectUri: `${location.origin}/auth/callback` });
|
|
114
|
+
// -> Studio /authorize (the player signs in there, consents if the app is not trusted)
|
|
115
|
+
// -> back to redirectUri?code=...&state=... -> handleSignInCallback() above
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`signIn` derives the hosted page from the API host you configured
|
|
119
|
+
(`ck.<tier>.crowdedkingdoms.com` -> `studio.<tier>.crowdedkingdoms.com/authorize`,
|
|
120
|
+
`localhost:3000` -> `localhost:3001`); pass `authorizeUrl` to override. Your
|
|
121
|
+
`redirectUri`'s origin must be one of the app's registered redirect URIs.
|
|
122
|
+
`beginEntry` / `completeEntry` are the same steps without the defaults.
|
|
123
|
+
|
|
124
|
+
**Direct sign-in + mint** (first-party page or non-browser). Use two clients: an
|
|
125
|
+
identity client (session token) and a per-game client (app token). They never
|
|
126
|
+
share a token store, and they need not share a URL — `mintAppToken` returns the
|
|
127
|
+
endpoint for the app's own datacenter.
|
|
87
128
|
|
|
88
129
|
```ts
|
|
89
|
-
|
|
90
|
-
const overworld = createCrowdyClient({
|
|
130
|
+
const identity = createCrowdyClient({
|
|
91
131
|
httpUrl: apiUrl,
|
|
92
132
|
tokenStore: new BrowserLocalStorageTokenStore('crowdyjs:session'),
|
|
93
133
|
});
|
|
94
|
-
|
|
95
|
-
// yields the session token. There is no dev bypass: devLogin was removed in 15.0.0.
|
|
96
|
-
await overworld.auth.requestLoginLink({ email, redirectUri });
|
|
97
|
-
await overworld.auth.completeLoginLink(tokenFromLink);
|
|
134
|
+
await identity.auth.login({ email, password }); // or register / magic link / social
|
|
98
135
|
|
|
99
|
-
|
|
100
|
-
const t = await overworld.portal.mintAppToken(appId);
|
|
136
|
+
const t = await identity.portal.mintAppToken(appId);
|
|
101
137
|
const game = createCrowdyClient({
|
|
102
138
|
httpUrl: t.gameApiUrl!,
|
|
103
139
|
wsUrl: t.gameApiWsUrl!,
|
|
@@ -107,24 +143,7 @@ game.setToken(t.token);
|
|
|
107
143
|
game.world(appId).subscribe({ actorUpdate: (n) => { /* ... */ } });
|
|
108
144
|
```
|
|
109
145
|
|
|
110
|
-
|
|
111
|
-
verifier never leaves the game origin:
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
// Game origin, on "enter": redirect to the Overworld authorize page.
|
|
115
|
-
location.assign(await game.portal.beginEntry({
|
|
116
|
-
appId, authorizeUrl: 'https://overworld.example.com/authorize',
|
|
117
|
-
redirectUri: location.origin + location.pathname,
|
|
118
|
-
}));
|
|
119
|
-
|
|
120
|
-
// Overworld /authorize page (holds the session token):
|
|
121
|
-
location.assign(await overworld.portal.handleAuthorizeRequest());
|
|
122
|
-
|
|
123
|
-
// Game origin, on callback boot: exchange code+verifier -> app token (stored).
|
|
124
|
-
const entered = await game.portal.completeEntry();
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Game-to-game routes through the Overworld for a fresh per-game token.
|
|
146
|
+
Game-to-game routes through Studio's hosted page for a fresh per-game token.
|
|
128
147
|
|
|
129
148
|
Notes:
|
|
130
149
|
|
package/dist/default-origin.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GENERATED — do not edit. The operator tooling's
|
|
3
|
-
* `sync-client-origins.mjs --write --tier
|
|
3
|
+
* `sync-client-origins.mjs --write --tier dev` writes this file, and
|
|
4
4
|
* `check-sdk-default-origin.mjs` refuses it when it names the wrong tier or a
|
|
5
5
|
* host that declaration does not carry. Regenerate; never hand-edit.
|
|
6
6
|
*
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
* than no default at all. Every fallback this SDK ever removed named a host that
|
|
11
11
|
* was already dead. A generated file plus a gate is what makes this one different.
|
|
12
12
|
*
|
|
13
|
-
* Source: the operator's per-tier public CK API origin declaration, tier '
|
|
13
|
+
* Source: the operator's per-tier public CK API origin declaration, tier 'dev'
|
|
14
14
|
*/
|
|
15
15
|
/** The tier this build of the SDK is published for. */
|
|
16
|
-
export declare const CROWDY_DEFAULT_TIER = "
|
|
16
|
+
export declare const CROWDY_DEFAULT_TIER = "dev";
|
|
17
17
|
/** The public CK API origin for that tier. */
|
|
18
|
-
export declare const CROWDY_DEFAULT_HTTP_ORIGIN = "https://ck.
|
|
18
|
+
export declare const CROWDY_DEFAULT_HTTP_ORIGIN = "https://ck.dev.crowdedkingdoms.com";
|
|
19
19
|
/** The same host over WebSocket. A scheme is composed; a hostname is looked up. */
|
|
20
|
-
export declare const CROWDY_DEFAULT_WS_ORIGIN = "wss://ck.
|
|
20
|
+
export declare const CROWDY_DEFAULT_WS_ORIGIN = "wss://ck.dev.crowdedkingdoms.com";
|
|
21
21
|
/** The bare hostname, for callers that need to compare rather than dial. */
|
|
22
|
-
export declare const CROWDY_DEFAULT_HOST = "ck.
|
|
22
|
+
export declare const CROWDY_DEFAULT_HOST = "ck.dev.crowdedkingdoms.com";
|
|
23
23
|
//# sourceMappingURL=default-origin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-origin.d.ts","sourceRoot":"","sources":["../src/default-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"default-origin.d.ts","sourceRoot":"","sources":["../src/default-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,8CAA8C;AAC9C,eAAO,MAAM,0BAA0B,uCAAuC,CAAC;AAE/E,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,qCAAqC,CAAC;AAE3E,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,+BAA+B,CAAC"}
|
package/dist/default-origin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GENERATED — do not edit. The operator tooling's
|
|
3
|
-
* `sync-client-origins.mjs --write --tier
|
|
3
|
+
* `sync-client-origins.mjs --write --tier dev` writes this file, and
|
|
4
4
|
* `check-sdk-default-origin.mjs` refuses it when it names the wrong tier or a
|
|
5
5
|
* host that declaration does not carry. Regenerate; never hand-edit.
|
|
6
6
|
*
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
* than no default at all. Every fallback this SDK ever removed named a host that
|
|
11
11
|
* was already dead. A generated file plus a gate is what makes this one different.
|
|
12
12
|
*
|
|
13
|
-
* Source: the operator's per-tier public CK API origin declaration, tier '
|
|
13
|
+
* Source: the operator's per-tier public CK API origin declaration, tier 'dev'
|
|
14
14
|
*/
|
|
15
15
|
/** The tier this build of the SDK is published for. */
|
|
16
|
-
export const CROWDY_DEFAULT_TIER = '
|
|
16
|
+
export const CROWDY_DEFAULT_TIER = 'dev';
|
|
17
17
|
/** The public CK API origin for that tier. */
|
|
18
|
-
export const CROWDY_DEFAULT_HTTP_ORIGIN = 'https://ck.
|
|
18
|
+
export const CROWDY_DEFAULT_HTTP_ORIGIN = 'https://ck.dev.crowdedkingdoms.com';
|
|
19
19
|
/** The same host over WebSocket. A scheme is composed; a hostname is looked up. */
|
|
20
|
-
export const CROWDY_DEFAULT_WS_ORIGIN = 'wss://ck.
|
|
20
|
+
export const CROWDY_DEFAULT_WS_ORIGIN = 'wss://ck.dev.crowdedkingdoms.com';
|
|
21
21
|
/** The bare hostname, for callers that need to compare rather than dial. */
|
|
22
|
-
export const CROWDY_DEFAULT_HOST = 'ck.
|
|
22
|
+
export const CROWDY_DEFAULT_HOST = 'ck.dev.crowdedkingdoms.com';
|
package/dist/domains/auth.d.ts
CHANGED
|
@@ -21,6 +21,16 @@ import type { AuthState } from '../auth-state.js';
|
|
|
21
21
|
*
|
|
22
22
|
* Part of the management surface.
|
|
23
23
|
*
|
|
24
|
+
* **FIRST-PARTY AND NON-BROWSER ONLY (ck-api v1.88.0, 2026-09-08).** Every
|
|
25
|
+
* public method here is served only to first-party browser origins (Studio,
|
|
26
|
+
* the Crowdy Games host) and to callers that send no `Origin` header at all
|
|
27
|
+
* (Node, CLI, tests, CrowdyCPP). From a browser game on its OWN domain they are
|
|
28
|
+
* refused with `HOSTED_SIGN_IN_REQUIRED` (`isHostedSignInRequiredError`), and
|
|
29
|
+
* that game signs its players in with `client.portal.signIn()` instead: the
|
|
30
|
+
* player types their password into Studio, never into the game, and the game
|
|
31
|
+
* comes back with an app-scoped token. If you are building a browser game, you
|
|
32
|
+
* want `client.portal`, not this namespace.
|
|
33
|
+
*
|
|
24
34
|
* **Public (no session):** {@link register}, {@link login},
|
|
25
35
|
* {@link requestLoginLink}, {@link completeLoginLink}, {@link socialLoginStart},
|
|
26
36
|
* {@link socialLoginComplete}, {@link availableLoginProviders},
|
|
@@ -164,10 +174,16 @@ export declare class AuthAPI {
|
|
|
164
174
|
/**
|
|
165
175
|
* Sign in with email + password; stores the session token on success.
|
|
166
176
|
*
|
|
177
|
+
* First-party and non-browser callers only: from a browser game on its own
|
|
178
|
+
* domain this is refused with `HOSTED_SIGN_IN_REQUIRED`; use
|
|
179
|
+
* `client.portal.signIn()` there (see the namespace comment).
|
|
180
|
+
*
|
|
167
181
|
* Throws when the credentials are wrong, and — separately — when the account
|
|
168
182
|
* has another verified sign-in method and the password has not yet been
|
|
169
183
|
* confirmed by email. {@link isPasswordUnconfirmedError} tells those apart,
|
|
170
|
-
* because they need different things from the user.
|
|
184
|
+
* because they need different things from the user. Ten wrong passwords for
|
|
185
|
+
* one address inside fifteen minutes answer `RATE_LIMITED`; the reset email
|
|
186
|
+
* is the way out.
|
|
171
187
|
*/
|
|
172
188
|
login(input: {
|
|
173
189
|
email: string;
|
|
@@ -176,12 +192,18 @@ export declare class AuthAPI {
|
|
|
176
192
|
/**
|
|
177
193
|
* Create an email + password account; stores the session token on success.
|
|
178
194
|
*
|
|
195
|
+
* First-party and non-browser callers only (`HOSTED_SIGN_IN_REQUIRED`
|
|
196
|
+
* otherwise; a browser game uses `client.portal.signIn()`, and Studio's
|
|
197
|
+
* hosted page offers sign-up).
|
|
198
|
+
*
|
|
179
199
|
* **A brand-new address gets a session immediately.** An address that already
|
|
180
|
-
* has an account does NOT
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
200
|
+
* has an account does NOT, and the server throws instead of returning a
|
|
201
|
+
* token, so the caller cannot treat "registered" and "signed in" as one
|
|
202
|
+
* outcome: an account that already has a password is left exactly as it was
|
|
203
|
+
* (ck-api v1.87.2; before that the password was overwritten, a takeover), and
|
|
204
|
+
* a password-less magic-link/social account gets the password attached
|
|
205
|
+
* pending email confirmation. Use {@link isAlreadyRegisteredError} to detect
|
|
206
|
+
* either and fall back to {@link login} or {@link requestLoginLink}.
|
|
185
207
|
*/
|
|
186
208
|
register(input: {
|
|
187
209
|
email: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/domains/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/domains/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/domains/auth.js
CHANGED
|
@@ -203,10 +203,16 @@ export class AuthAPI {
|
|
|
203
203
|
/**
|
|
204
204
|
* Sign in with email + password; stores the session token on success.
|
|
205
205
|
*
|
|
206
|
+
* First-party and non-browser callers only: from a browser game on its own
|
|
207
|
+
* domain this is refused with `HOSTED_SIGN_IN_REQUIRED`; use
|
|
208
|
+
* `client.portal.signIn()` there (see the namespace comment).
|
|
209
|
+
*
|
|
206
210
|
* Throws when the credentials are wrong, and — separately — when the account
|
|
207
211
|
* has another verified sign-in method and the password has not yet been
|
|
208
212
|
* confirmed by email. {@link isPasswordUnconfirmedError} tells those apart,
|
|
209
|
-
* because they need different things from the user.
|
|
213
|
+
* because they need different things from the user. Ten wrong passwords for
|
|
214
|
+
* one address inside fifteen minutes answer `RATE_LIMITED`; the reset email
|
|
215
|
+
* is the way out.
|
|
210
216
|
*/
|
|
211
217
|
async login(input) {
|
|
212
218
|
const data = await this.graphql.request(LoginDocument, {
|
|
@@ -219,12 +225,18 @@ export class AuthAPI {
|
|
|
219
225
|
/**
|
|
220
226
|
* Create an email + password account; stores the session token on success.
|
|
221
227
|
*
|
|
228
|
+
* First-party and non-browser callers only (`HOSTED_SIGN_IN_REQUIRED`
|
|
229
|
+
* otherwise; a browser game uses `client.portal.signIn()`, and Studio's
|
|
230
|
+
* hosted page offers sign-up).
|
|
231
|
+
*
|
|
222
232
|
* **A brand-new address gets a session immediately.** An address that already
|
|
223
|
-
* has an account does NOT
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
233
|
+
* has an account does NOT, and the server throws instead of returning a
|
|
234
|
+
* token, so the caller cannot treat "registered" and "signed in" as one
|
|
235
|
+
* outcome: an account that already has a password is left exactly as it was
|
|
236
|
+
* (ck-api v1.87.2; before that the password was overwritten, a takeover), and
|
|
237
|
+
* a password-less magic-link/social account gets the password attached
|
|
238
|
+
* pending email confirmation. Use {@link isAlreadyRegisteredError} to detect
|
|
239
|
+
* either and fall back to {@link login} or {@link requestLoginLink}.
|
|
228
240
|
*/
|
|
229
241
|
async register(input) {
|
|
230
242
|
const data = await this.graphql.request(RegisterDocument, {
|
package/dist/domains/portal.d.ts
CHANGED
|
@@ -13,15 +13,29 @@
|
|
|
13
13
|
* client whose token store holds that game's app token.
|
|
14
14
|
*
|
|
15
15
|
* Browser handoff (cross-origin) is an OAuth2 Authorization-Code + PKCE flow:
|
|
16
|
-
* 1. game origin: `
|
|
17
|
-
* `/authorize` page carrying a PKCE
|
|
18
|
-
*
|
|
16
|
+
* 1. game origin: `signIn()` (or the lower-level `beginEntry()`) -> redirect
|
|
17
|
+
* the player to Studio's hosted `/authorize` page carrying a PKCE
|
|
18
|
+
* challenge + the game's redirect URI.
|
|
19
|
+
* 2. Studio origin (holds the session): `handleAuthorizeRequest()` ->
|
|
19
20
|
* mints a one-time code and redirects back to the game.
|
|
20
|
-
* 3. game origin: `
|
|
21
|
-
* it kept locally) for an app
|
|
21
|
+
* 3. game origin: `handleSignInCallback()` (or `completeEntry()`) ->
|
|
22
|
+
* exchanges the code (+ the PKCE verifier it kept locally) for an app
|
|
23
|
+
* token and stores it.
|
|
22
24
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
* THIS IS THE ONLY WAY A BROWSER GAME ON ITS OWN DOMAIN SIGNS A PLAYER IN
|
|
26
|
+
* (ck-api v1.88.0, 2026-09-08). The direct sign-in mutations behind
|
|
27
|
+
* `client.auth.login` / `register` / magic link / social are served only to
|
|
28
|
+
* first-party origins (Studio, the Crowdy Games host); from any other browser
|
|
29
|
+
* origin they are refused with `HOSTED_SIGN_IN_REQUIRED`
|
|
30
|
+
* ({@link isHostedSignInRequiredError}). The reason is the player's password:
|
|
31
|
+
* a form on a customer's domain that collects it is indistinguishable, to the
|
|
32
|
+
* platform and to the player, from a phishing page. Hosted sign-in means the
|
|
33
|
+
* password is only ever typed into Studio and the game receives a token
|
|
34
|
+
* confined to itself.
|
|
35
|
+
*
|
|
36
|
+
* Native / first-party / non-browser callers (no `Origin` header) can skip
|
|
37
|
+
* the redirect dance: sign in with `client.auth.*` and call
|
|
38
|
+
* `mintAppToken(appId)` directly with the session token.
|
|
25
39
|
*/
|
|
26
40
|
import type { GraphQLClient } from '../client.js';
|
|
27
41
|
import type { SessionStore } from '../session.js';
|
|
@@ -121,13 +135,54 @@ export declare class PortalConsentRequiredError extends Error {
|
|
|
121
135
|
export interface BeginEntryParams {
|
|
122
136
|
/** Target app id (decimal string). */
|
|
123
137
|
appId: string;
|
|
124
|
-
/** The
|
|
138
|
+
/** The hosted sign-in page, e.g. `https://studio.dev.crowdedkingdoms.com/authorize`. */
|
|
125
139
|
authorizeUrl: string;
|
|
126
|
-
/** Where
|
|
140
|
+
/** Where Studio should send the player back (this game's callback). */
|
|
127
141
|
redirectUri: string;
|
|
128
142
|
/** Optional CSRF/correlation state; one is generated if omitted. */
|
|
129
143
|
state?: string;
|
|
130
144
|
}
|
|
145
|
+
export interface SignInParams {
|
|
146
|
+
/** Target app id (decimal string). */
|
|
147
|
+
appId: string;
|
|
148
|
+
/**
|
|
149
|
+
* Where Studio sends the player back (this game's callback URL). Its ORIGIN
|
|
150
|
+
* must be one of the app's registered redirect URIs (Studio > Apps > client
|
|
151
|
+
* settings), which is also what puts the origin on the API's CORS allow-list.
|
|
152
|
+
*/
|
|
153
|
+
redirectUri: string;
|
|
154
|
+
/**
|
|
155
|
+
* The hosted sign-in page. Defaults to the Studio `/authorize` of the tier
|
|
156
|
+
* this client dials ({@link defaultHostedSignInUrl}); pass it explicitly for
|
|
157
|
+
* a self-hosted or unusual layout.
|
|
158
|
+
*/
|
|
159
|
+
authorizeUrl?: string;
|
|
160
|
+
/** Optional CSRF/correlation state; one is generated if omitted. */
|
|
161
|
+
state?: string;
|
|
162
|
+
/**
|
|
163
|
+
* `false` to only compute and return the URL without navigating (tests,
|
|
164
|
+
* SSR, a custom link). Default: navigate with `location.assign` when a
|
|
165
|
+
* browser `location` exists.
|
|
166
|
+
*/
|
|
167
|
+
navigate?: boolean;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The hosted sign-in page for the tier a GraphQL endpoint belongs to.
|
|
171
|
+
*
|
|
172
|
+
* The CK API answers on `ck.<tier>.crowdedkingdoms.com` and Studio on
|
|
173
|
+
* `studio.<tier>.crowdedkingdoms.com` (infra-control-plane `dns-tier.ts`,
|
|
174
|
+
* `CK_STUDIO_PUBLIC_HOST_BY_TIER`), so the first label is the only difference;
|
|
175
|
+
* locally the API is on :3000 and Studio on :3001. Anything else has no
|
|
176
|
+
* convention to lean on and the caller must pass `authorizeUrl`.
|
|
177
|
+
*/
|
|
178
|
+
export declare function defaultHostedSignInUrl(graphqlEndpoint: string): string;
|
|
179
|
+
/**
|
|
180
|
+
* `HOSTED_SIGN_IN_REQUIRED`: a direct sign-in mutation (`auth.login`,
|
|
181
|
+
* `auth.register`, magic link, social, reset...) was called from a browser
|
|
182
|
+
* origin that is not first-party. The remedy is {@link PortalAPI.signIn}.
|
|
183
|
+
* ck-api v1.88.0.
|
|
184
|
+
*/
|
|
185
|
+
export declare function isHostedSignInRequiredError(error: unknown): boolean;
|
|
131
186
|
export declare class PortalAPI {
|
|
132
187
|
private readonly api;
|
|
133
188
|
private readonly session;
|
|
@@ -178,16 +233,45 @@ export declare class PortalAPI {
|
|
|
178
233
|
clientType?: string;
|
|
179
234
|
launchUrl?: string;
|
|
180
235
|
}): Promise<PortalConsentState>;
|
|
236
|
+
/**
|
|
237
|
+
* Send the player to Studio's hosted sign-in and come back with an app token.
|
|
238
|
+
*
|
|
239
|
+
* ```ts
|
|
240
|
+
* // on the "Sign in with Crowded Kingdoms" button:
|
|
241
|
+
* await client.portal.signIn({ appId, redirectUri: `${location.origin}/auth/callback` });
|
|
242
|
+
*
|
|
243
|
+
* // on /auth/callback (or unconditionally on boot -- it is a no-op without ?code=):
|
|
244
|
+
* const entered = await client.portal.handleSignInCallback();
|
|
245
|
+
* if (entered) start(entered); // client now holds an app-scoped token
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* This is `beginEntry()` with the hosted page defaulted to the tier's Studio
|
|
249
|
+
* and the navigation done for you. It is the ONLY sign-in a browser game on
|
|
250
|
+
* its own domain can use: `client.auth.login` and friends are refused from a
|
|
251
|
+
* non-first-party origin with `HOSTED_SIGN_IN_REQUIRED`. Resolves to the URL
|
|
252
|
+
* navigated to (useful when `navigate: false`).
|
|
253
|
+
*/
|
|
254
|
+
signIn(params: SignInParams): Promise<string>;
|
|
255
|
+
/**
|
|
256
|
+
* The return leg of {@link signIn}: exchange the `?code=` Studio sent back
|
|
257
|
+
* for an app-scoped token and store it on this client. Returns null when the
|
|
258
|
+
* URL carries no code, so it is safe to call on every boot. In a browser it
|
|
259
|
+
* also strips `code` and `state` from the address bar, so a reload does not
|
|
260
|
+
* try to spend a code that has already been used (a replay is refused).
|
|
261
|
+
*/
|
|
262
|
+
handleSignInCallback(search?: string): Promise<AppTokenResponse | null>;
|
|
181
263
|
/**
|
|
182
264
|
* Destination-game side, step 1: generate a PKCE pair, persist the verifier,
|
|
183
|
-
* and return the
|
|
184
|
-
* `window.location.assign(url)`.
|
|
265
|
+
* and return the hosted sign-in URL to navigate to. The caller does
|
|
266
|
+
* `window.location.assign(url)`. Prefer {@link signIn}, which does both.
|
|
185
267
|
*/
|
|
186
268
|
beginEntry(params: BeginEntryParams): Promise<string>;
|
|
187
269
|
/**
|
|
188
|
-
*
|
|
270
|
+
* Studio (identity) side: handle an incoming `/authorize` request. Reads the
|
|
189
271
|
* game's params from the URL, mints a code with the session token, and returns
|
|
190
|
-
* the URL to redirect the player back to (carrying `code` + `state`).
|
|
272
|
+
* the URL to redirect the player back to (carrying `code` + `state`). Studio's
|
|
273
|
+
* own `/authorize` page is the production caller; a self-hosted identity page
|
|
274
|
+
* may use it too.
|
|
191
275
|
*/
|
|
192
276
|
handleAuthorizeRequest(search?: string, options?: {
|
|
193
277
|
grantConsent?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/domains/portal.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/domains/portal.ts"],"names":[],"mappings":"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"}
|