screeps-connectivity 0.12.0 → 0.13.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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/http/auth/TokenAuth.ts +10 -2
- package/src/http/fetchServerVersion.ts +14 -1
- package/src/index.ts +2 -1
- package/src/types/game.ts +12 -0
- package/tests/http/HttpClient.test.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 94a6658: Add Discord OAuth login: getDiscordFeature() helper in screeps-connectivity, and a "Login with Discord" button in the web and desktop login forms.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 46b5e2d: Fix Steam/password logins silently expiring after ~5 minutes on screepsmod-auth servers. These logins reconnect via a rotating, TTL-limited session token, but `TokenAuth` was hard-coded to ignore the server-issued `X-Token`, so the client kept replaying the original token until the server expired it — surfacing as a sudden `401` on `/api/user/world-status` (and every other authed request) even while actively using the client.
|
|
12
|
+
|
|
13
|
+
`TokenAuth` now accepts `supportsTokenRefresh` (default `false`, preserving durable personal-API-token behavior). The client enables it for Steam/password-derived session tokens so the rotated `X-Token` is adopted on every response, keeping the session alive.
|
|
14
|
+
|
|
3
15
|
## 0.12.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -2,11 +2,19 @@ import type { AuthStrategy } from './AuthStrategy.js'
|
|
|
2
2
|
import type { HttpClient } from '../HttpClient.js'
|
|
3
3
|
|
|
4
4
|
export class TokenAuth implements AuthStrategy {
|
|
5
|
-
readonly supportsTokenRefresh
|
|
5
|
+
readonly supportsTokenRefresh: boolean
|
|
6
6
|
private readonly token: string
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @param opts.supportsTokenRefresh Set true when the token is a rotating,
|
|
10
|
+
* TTL-limited session token (e.g. obtained from a screepsmod-auth steam/password
|
|
11
|
+
* exchange) so the client adopts the server-issued `X-Token` and the session
|
|
12
|
+
* stays alive. Leave false (default) for a durable personal API token, which
|
|
13
|
+
* must never be replaced by a server-issued token.
|
|
14
|
+
*/
|
|
15
|
+
constructor(opts: { token: string; supportsTokenRefresh?: boolean }) {
|
|
9
16
|
this.token = opts.token
|
|
17
|
+
this.supportsTokenRefresh = opts.supportsTokenRefresh ?? false
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
async authenticate(_http: HttpClient): Promise<string> {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ServerVersion } from '../types/game.js'
|
|
2
|
-
import type { ScreepsmodAuthFeature, ServerFeature, XxscreepsModClientFeature } from '../types/game.js'
|
|
2
|
+
import type { DiscordFeature, ScreepsmodAuthFeature, ServerFeature, XxscreepsModClientFeature } from '../types/game.js'
|
|
3
3
|
import type { ApiAuthMeResponse, ApiAuthModInfoResponse, ApiRegisterCheckResponse } from '../types/api.js'
|
|
4
4
|
import { getFetch } from './fetchFn.js'
|
|
5
5
|
|
|
@@ -193,3 +193,16 @@ export function getScreepsmodAuth(version: ServerVersion): ScreepsmodAuthFeature
|
|
|
193
193
|
export function getXxscreepsModClientFeature(version: ServerVersion): XxscreepsModClientFeature | undefined {
|
|
194
194
|
return getServerFeature<XxscreepsModClientFeature>(version, 'xxscreeps-mod-client')
|
|
195
195
|
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Returns the `discord` feature entry (published by `xxscreeps-mod-discord`) if
|
|
199
|
+
* present, otherwise `undefined`. Use `discordLogin` to gate the "Login with
|
|
200
|
+
* Discord" button before showing it.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* const discord = getDiscordFeature(version)
|
|
204
|
+
* if (discord?.discordLogin) showDiscordButton()
|
|
205
|
+
*/
|
|
206
|
+
export function getDiscordFeature(version: ServerVersion): DiscordFeature | undefined {
|
|
207
|
+
return getServerFeature<DiscordFeature>(version, 'discord')
|
|
208
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -33,6 +33,7 @@ export type {
|
|
|
33
33
|
ServerFeature,
|
|
34
34
|
ScreepsmodAuthFeature,
|
|
35
35
|
XxscreepsModClientFeature,
|
|
36
|
+
DiscordFeature,
|
|
36
37
|
ShardInfo,
|
|
37
38
|
WorldInfo,
|
|
38
39
|
Badge,
|
|
@@ -41,7 +42,7 @@ export type {
|
|
|
41
42
|
MapVisualEntry,
|
|
42
43
|
} from './types/game.js'
|
|
43
44
|
|
|
44
|
-
export { fetchServerVersion, fetchAuthModInfo, checkUsername, checkEmail, registerUser, fetchAuthMeWithToken, completeProviderRegistration, getServerFeature, getScreepsmodAuth, getXxscreepsModClientFeature } from './http/fetchServerVersion.js'
|
|
45
|
+
export { fetchServerVersion, fetchAuthModInfo, checkUsername, checkEmail, registerUser, fetchAuthMeWithToken, completeProviderRegistration, getServerFeature, getScreepsmodAuth, getXxscreepsModClientFeature, getDiscordFeature } from './http/fetchServerVersion.js'
|
|
45
46
|
export type { ApiAuthModInfoResponse } from './http/fetchServerVersion.js'
|
|
46
47
|
export type { ApiAuthMeResponse, RoomHistoryChunk, ApiRoomDecorationsResponse, ApiRoomDecorationItem, ApiRoomDecorationDef, ApiRoomDecorationGraphic, ApiRoomDecorationActive, ApiUserOverviewResponse, ApiUserOverviewTotals, ApiUserRoomsResponse, ApiUserStatsResponse, ApiLeaderboardFindResponse, ApiUserMoneyHistoryResponse, ApiMarketOrder, ApiMarketOrdersIndexResponse, ApiMarketOrdersResponse, ApiMarketMyOrdersResponse, ApiMarketStat, ApiMarketStatsResponse, ApiPowerCreep, ApiPowerCreepsListResponse, ApiUserMessage, ApiUserMessagesListResponse, ApiUserMessagesIndexEntry, ApiUserMessagesIndexResponse, ApiUserMessagesUnreadCountResponse, ApiUserFindResponse } from './types/api.js'
|
|
47
48
|
export { ROOM_DECORATIONS_MOCK } from './mocks/roomDecorations.js'
|
package/src/types/game.ts
CHANGED
|
@@ -125,6 +125,18 @@ export interface XxscreepsModClientFeature extends ServerFeature {
|
|
|
125
125
|
steamLogin: boolean
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Published by `xxscreeps-mod-discord` at `/api/version` when Discord login is
|
|
130
|
+
* configured. `discordLogin` gates the UI; `loginUrl` is the OAuth entry point
|
|
131
|
+
* (a popup navigates there to start the flow).
|
|
132
|
+
*/
|
|
133
|
+
export interface DiscordFeature extends ServerFeature {
|
|
134
|
+
name: 'discord'
|
|
135
|
+
version: number
|
|
136
|
+
discordLogin: boolean
|
|
137
|
+
loginUrl: string
|
|
138
|
+
}
|
|
139
|
+
|
|
128
140
|
export interface ServerVersion {
|
|
129
141
|
ok: number
|
|
130
142
|
package: number
|
|
@@ -98,6 +98,16 @@ describe('HttpClient', () => {
|
|
|
98
98
|
expect(http.token).toBe('my-static-token')
|
|
99
99
|
})
|
|
100
100
|
|
|
101
|
+
it('DOES update token from x-token header when TokenAuth opts into refresh (session token)', async () => {
|
|
102
|
+
fetchMock.mockResolvedValue(mockResponse({ ok: 1 }, {
|
|
103
|
+
headers: { 'content-type': 'application/json', 'x-token': 'rotated' },
|
|
104
|
+
}))
|
|
105
|
+
const http = new HttpClient({ url: 'http://test.local', auth: new TokenAuth({ token: 'session-token', supportsTokenRefresh: true }) })
|
|
106
|
+
http.token = 'session-token'
|
|
107
|
+
await http.request('GET', '/api/version')
|
|
108
|
+
expect(http.token).toBe('rotated')
|
|
109
|
+
})
|
|
110
|
+
|
|
101
111
|
it('retries once on 401 after re-authenticating', async () => {
|
|
102
112
|
let calls = 0
|
|
103
113
|
fetchMock.mockImplementation(() => {
|