Skip to content

Commit 779c355

Browse files
authored
Merge branch 'master' into dependabot/npm_and_yarn/example/react/nanoid-3.3.8
2 parents 2c70846 + 80f88e4 commit 779c355

File tree

11 files changed

+242
-138
lines changed

11 files changed

+242
-138
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
matrix:
1717
os: [ubuntu-latest]
18-
node: ['18', '20']
18+
node: ["18", "20"]
1919

2020
runs-on: ${{ matrix.os }}
2121

@@ -35,3 +35,9 @@ jobs:
3535
- name: Run tests
3636
run: |
3737
npm t
38+
39+
- name: Upload coverage results to Coveralls
40+
uses: coverallsapp/github-action@master
41+
with:
42+
github-token: ${{ secrets.GITHUB_TOKEN }}
43+
path-to-lcov: ./test/coverage/lcov.info

.github/workflows/dogfooding.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Dogfooding Check
2+
3+
on:
4+
pull_request_review:
5+
types: [submitted, edited]
6+
7+
pull_request_target:
8+
types:
9+
- opened
10+
branches:
11+
- '*'
12+
13+
jobs:
14+
check_dogfooding:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
if: github.event.pull_request.base.ref == 'master' && github.event.pull_request.head.ref == 'release-please--branches--master'
19+
with:
20+
ref: master # used to identify the latest RC version via git describe --tags --match rc*
21+
fetch-depth: 0
22+
23+
- if: github.event.pull_request.base.ref == 'master' && github.event.pull_request.head.ref == 'release-please--branches--master'
24+
run: |
25+
set -ex
26+
27+
# finds the latest RC version on master
28+
RELEASE_VERSION=@supabase/auth-js@$(node -e "const a = '$(git describe --tags --match rc*)'.replace(/^rc/, '').split('-'); console.log(a[0] + '-' + a[1]);")
29+
30+
# use some clever Ruby magic to extract the snapshots['@supabase/auth-js@...'] version from the pnpm-lock.yaml file
31+
STUDIO_VERSION=$(curl 'https://raw.githubusercontent.com/supabase/supabase/refs/heads/master/pnpm-lock.yaml' | ruby -e 'require("yaml"); l = YAML.load(STDIN); puts(l["snapshots"].find { |k, v| k.start_with? "@supabase/auth-js" }.first)')
32+
33+
echo "Expecting RC version $RELEASE_VERSION to be used in Supabase Studio."
34+
35+
if [ "$STUDIO_VERSION" != "$RELEASE_VERSION" ]
36+
then
37+
echo "Version in Supabase Studio is not the latest release candidate. Please release this RC first to proof the release before merging this PR."
38+
exit 1
39+
fi
40+
41+
echo "Release away!"
42+
exit 0
43+
44+
- if: github.event.pull_request.base.ref != 'master' || github.event.pull_request.head.ref != 'release-please--branches--master'
45+
run: |
46+
set -ex
47+
48+
echo "This PR is not subject to dogfooding checks."
49+
exit 0
50+

.github/workflows/release.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
release_please:
1111
runs-on: ubuntu-latest
1212
permissions:
13+
id-token: write
1314
contents: write
1415
pull-requests: write
1516
steps:
@@ -102,16 +103,17 @@ jobs:
102103
103104
echo "Publishing auth-js now..."
104105
105-
npm publish --tag "$DIST_TAG"
106+
npm publish --provenance --tag "$DIST_TAG"
106107
107108
echo "Publishing gotrue-js now..."
108109
109110
for f in package.json package-lock.json
110111
do
111-
sed -i 's|\(["/]\)auth-js|\1gotrue-js|g' "$f"
112+
# only replace name not repository, homepage, etc.
113+
sed -i 's|\("name":[[:space:]]*"@supabase/\)auth-js|\1gotrue-js|g' "$f"
112114
done
113115
114-
npm publish --tag "$DIST_TAG"
116+
npm publish --provenance --tag "$DIST_TAG"
115117
116118
- name: Create GitHub release and branches
117119
if: ${{ steps.release.outputs.release_created == 'true' || steps.release.outputs.prs_created == 'true' }}

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Changelog
22

3+
## [2.67.3](https://github.com/supabase/auth-js/compare/v2.67.2...v2.67.3) (2024-12-17)
4+
5+
6+
### Bug Fixes
7+
8+
* return redirect errors early ([#1003](https://github.com/supabase/auth-js/issues/1003)) ([9751b80](https://github.com/supabase/auth-js/commit/9751b8029b4235a63dcb525e7ce7cc942c85daf5))
9+
10+
## [2.67.2](https://github.com/supabase/auth-js/compare/v2.67.1...v2.67.2) (2024-12-16)
11+
12+
13+
### Bug Fixes
14+
15+
* `isBrowser()` to include check on `window` ([#982](https://github.com/supabase/auth-js/issues/982)) ([645f224](https://github.com/supabase/auth-js/commit/645f22447e68ba13e43e359d1524e95fe025d771))
16+
17+
## [2.67.1](https://github.com/supabase/auth-js/compare/v2.67.0...v2.67.1) (2024-12-13)
18+
19+
20+
### Bug Fixes
21+
22+
* revert [#992](https://github.com/supabase/auth-js/issues/992) and [#993](https://github.com/supabase/auth-js/issues/993) ([#999](https://github.com/supabase/auth-js/issues/999)) ([12b2848](https://github.com/supabase/auth-js/commit/12b2848237854f3d70b9989920ad50e2c4186fff))
23+
324
## [2.67.0](https://github.com/supabase/auth-js/compare/v2.66.1...v2.67.0) (2024-12-12)
425

526

example/react/package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"main": "dist/main/index.js",
2121
"module": "dist/module/index.js",
2222
"types": "dist/module/index.d.ts",
23-
"repository": "supabase/auth-js",
23+
"repository": "github:supabase/auth-js",
2424
"scripts": {
2525
"clean": "rimraf dist docs",
2626
"coverage": "echo \"run npm test\"",
@@ -30,7 +30,7 @@
3030
"build:module": "tsc -p tsconfig.module.json",
3131
"lint": "eslint ./src/**/* test/**/*.test.ts",
3232
"test": "run-s test:clean test:infra test:suite test:clean",
33-
"test:suite": "jest --runInBand",
33+
"test:suite": "jest --runInBand --coverage",
3434
"test:infra": "cd infra && docker compose down && docker compose pull && docker compose up -d && sleep 30",
3535
"test:clean": "cd infra && docker compose down",
3636
"docs": "typedoc src/index.ts --out docs/v2 --excludePrivate --excludeProtected",

src/GoTrueClient.ts

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import GoTrueAdminApi from './GoTrueAdminApi'
2-
import { DEFAULT_HEADERS, EXPIRY_MARGIN, GOTRUE_URL, STORAGE_KEY } from './lib/constants'
2+
import {
3+
DEFAULT_HEADERS,
4+
EXPIRY_MARGIN_MS,
5+
AUTO_REFRESH_TICK_DURATION_MS,
6+
AUTO_REFRESH_TICK_THRESHOLD,
7+
GOTRUE_URL,
8+
STORAGE_KEY,
9+
} from './lib/constants'
310
import {
411
AuthError,
512
AuthImplicitGrantRedirectError,
@@ -89,13 +96,11 @@ import type {
8996
LockFunc,
9097
UserIdentity,
9198
SignInAnonymouslyCredentials,
92-
} from './lib/types'
93-
import {
9499
MFAEnrollTOTPParams,
95100
MFAEnrollPhoneParams,
96101
AuthMFAEnrollTOTPResponse,
97102
AuthMFAEnrollPhoneResponse,
98-
} from './lib/internal-types'
103+
} from './lib/types'
99104

100105
polyfillGlobalThis() // Make "globalThis" available
101106

@@ -111,13 +116,6 @@ const DEFAULT_OPTIONS: Omit<Required<GoTrueClientOptions>, 'fetch' | 'storage' |
111116
hasCustomAuthorizationHeader: false,
112117
}
113118

114-
/** Current session will be checked for refresh at this interval. */
115-
const AUTO_REFRESH_TICK_DURATION = 30 * 1000
116-
117-
/**
118-
* A token refresh will be attempted this many ticks before the current session expires. */
119-
const AUTO_REFRESH_TICK_THRESHOLD = 3
120-
121119
async function lockNoOp<R>(name: string, acquireTimeout: number, fn: () => Promise<R>): Promise<R> {
122120
return await fn()
123121
}
@@ -307,8 +305,22 @@ export default class GoTrueClient {
307305
*/
308306
private async _initialize(): Promise<InitializeResult> {
309307
try {
310-
if (isBrowser() && this.detectSessionInUrl) {
311-
const { data, error } = await this._getSessionFromURL()
308+
const params = parseParametersFromURL(window.location.href)
309+
let callbackUrlType = 'none'
310+
if (this._isImplicitGrantCallback(params)) {
311+
callbackUrlType = 'implicit'
312+
} else if (await this._isPKCECallback(params)) {
313+
callbackUrlType = 'pkce'
314+
}
315+
316+
/**
317+
* Attempt to get the session from the URL only if these conditions are fulfilled
318+
*
319+
* Note: If the URL isn't one of the callback url types (implicit or pkce),
320+
* then there could be an existing session so we don't want to prematurely remove it
321+
*/
322+
if (isBrowser() && this.detectSessionInUrl && callbackUrlType !== 'none') {
323+
const { data, error } = await this._getSessionFromURL(params, callbackUrlType)
312324
if (error) {
313325
this._debug('#_initialize()', 'error detecting session from URL', error)
314326

@@ -1095,8 +1107,13 @@ export default class GoTrueClient {
10951107
return { data: { session: null }, error: null }
10961108
}
10971109

1110+
// A session is considered expired before the access token _actually_
1111+
// expires. When the autoRefreshToken option is off (or when the tab is
1112+
// in the background), very eager users of getSession() -- like
1113+
// realtime-js -- might send a valid JWT which will expire by the time it
1114+
// reaches the server.
10981115
const hasExpired = currentSession.expires_at
1099-
? currentSession.expires_at <= Date.now() / 1000
1116+
? currentSession.expires_at * 1000 - Date.now() < EXPIRY_MARGIN_MS
11001117
: false
11011118

11021119
this._debug(
@@ -1411,7 +1428,10 @@ export default class GoTrueClient {
14111428
/**
14121429
* Gets the session data from a URL string
14131430
*/
1414-
private async _getSessionFromURL(): Promise<
1431+
private async _getSessionFromURL(
1432+
params: { [parameter: string]: string },
1433+
callbackUrlType: string
1434+
): Promise<
14151435
| {
14161436
data: { session: Session; redirectType: string | null }
14171437
error: null
@@ -1421,8 +1441,6 @@ export default class GoTrueClient {
14211441
try {
14221442
if (!isBrowser()) throw new AuthImplicitGrantRedirectError('No browser detected.')
14231443

1424-
const params = parseParametersFromURL(window.location.href)
1425-
14261444
// If there's an error in the URL, it doesn't matter what flow it is, we just return the error.
14271445
if (params.error || params.error_description || params.error_code) {
14281446
// The error class returned implies that the redirect is from an implicit grant flow
@@ -1436,23 +1454,25 @@ export default class GoTrueClient {
14361454
)
14371455
}
14381456

1439-
const isRedirectFromImplicitGrantFlow = this._isImplicitGrantFlow(params)
1440-
const isRedirectFromPKCEFlow = await this._isPKCEFlow(params)
1441-
14421457
// Checks for mismatches between the flowType initialised in the client and the URL parameters
1443-
if (!isRedirectFromImplicitGrantFlow && !isRedirectFromPKCEFlow) {
1444-
if (this.flowType === 'implicit') {
1445-
throw new AuthImplicitGrantRedirectError('Not a valid implicit grant flow url.')
1446-
} else if (this.flowType === 'pkce') {
1447-
throw new AuthPKCEGrantCodeExchangeError('Not a valid PKCE flow url.')
1448-
} else {
1449-
throw new AuthError('Invalid flow type.')
1450-
}
1458+
switch (callbackUrlType) {
1459+
case 'implicit':
1460+
if (this.flowType === 'pkce') {
1461+
throw new AuthPKCEGrantCodeExchangeError('Not a valid PKCE flow url.')
1462+
}
1463+
break
1464+
case 'pkce':
1465+
if (this.flowType === 'implicit') {
1466+
throw new AuthImplicitGrantRedirectError('Not a valid implicit grant flow url.')
1467+
}
1468+
break
1469+
default:
1470+
// there's no mismatch so we continue
14511471
}
14521472

14531473
// Since this is a redirect for PKCE, we attempt to retrieve the code from the URL for the code exchange
1454-
if (isRedirectFromPKCEFlow) {
1455-
this._debug('#_initialize()', 'begin', 'is PKCE flow', isRedirectFromPKCEFlow)
1474+
if (callbackUrlType === 'pkce') {
1475+
this._debug('#_initialize()', 'begin', 'is PKCE flow', true)
14561476
if (!params.code) throw new AuthPKCEGrantCodeExchangeError('No code detected.')
14571477
const { data, error } = await this._exchangeCodeForSession(params.code)
14581478
if (error) throw error
@@ -1488,7 +1508,7 @@ export default class GoTrueClient {
14881508
}
14891509

14901510
const actuallyExpiresIn = expiresAt - timeNow
1491-
if (actuallyExpiresIn * 1000 <= AUTO_REFRESH_TICK_DURATION) {
1511+
if (actuallyExpiresIn * 1000 <= AUTO_REFRESH_TICK_DURATION_MS) {
14921512
console.warn(
14931513
`@supabase/gotrue-js: Session as retrieved from URL expires in ${actuallyExpiresIn}s, should have been closer to ${expiresIn}s`
14941514
)
@@ -1542,20 +1562,20 @@ export default class GoTrueClient {
15421562
/**
15431563
* Checks if the current URL contains parameters given by an implicit oauth grant flow (https://www.rfc-editor.org/rfc/rfc6749.html#section-4.2)
15441564
*/
1545-
private _isImplicitGrantFlow(params: { [parameter: string]: string }): boolean {
1546-
return !!((params.access_token || params.error_description) && this.flowType === 'implicit')
1565+
private _isImplicitGrantCallback(params: { [parameter: string]: string }): boolean {
1566+
return Boolean(params.access_token || params.error_description)
15471567
}
15481568

15491569
/**
15501570
* Checks if the current URL and backing storage contain parameters given by a PKCE flow
15511571
*/
1552-
private async _isPKCEFlow(params: { [parameter: string]: string }): Promise<boolean> {
1572+
private async _isPKCECallback(params: { [parameter: string]: string }): Promise<boolean> {
15531573
const currentStorageContent = await getItemAsync(
15541574
this.storage,
15551575
`${this.storageKey}-code-verifier`
15561576
)
15571577

1558-
return !!(params.code && currentStorageContent && this.flowType === 'pkce')
1578+
return !!(params.code && currentStorageContent)
15591579
}
15601580

15611581
/**
@@ -1835,7 +1855,7 @@ export default class GoTrueClient {
18351855
error &&
18361856
isAuthRetryableFetchError(error) &&
18371857
// retryable only if the request can be sent before the backoff overflows the tick duration
1838-
Date.now() + nextBackOffInterval - startedAt < AUTO_REFRESH_TICK_DURATION
1858+
Date.now() + nextBackOffInterval - startedAt < AUTO_REFRESH_TICK_DURATION_MS
18391859
)
18401860
}
18411861
)
@@ -1908,12 +1928,12 @@ export default class GoTrueClient {
19081928
return
19091929
}
19101930

1911-
const timeNow = Math.round(Date.now() / 1000)
1912-
const expiresWithMargin = (currentSession.expires_at ?? Infinity) < timeNow + EXPIRY_MARGIN
1931+
const expiresWithMargin =
1932+
(currentSession.expires_at ?? Infinity) * 1000 - Date.now() < EXPIRY_MARGIN_MS
19131933

19141934
this._debug(
19151935
debugName,
1916-
`session has${expiresWithMargin ? '' : ' not'} expired with margin of ${EXPIRY_MARGIN}s`
1936+
`session has${expiresWithMargin ? '' : ' not'} expired with margin of ${EXPIRY_MARGIN_MS}s`
19171937
)
19181938

19191939
if (expiresWithMargin) {
@@ -2086,7 +2106,7 @@ export default class GoTrueClient {
20862106

20872107
this._debug('#_startAutoRefresh()')
20882108

2089-
const ticker = setInterval(() => this._autoRefreshTokenTick(), AUTO_REFRESH_TICK_DURATION)
2109+
const ticker = setInterval(() => this._autoRefreshTokenTick(), AUTO_REFRESH_TICK_DURATION_MS)
20902110
this.autoRefreshTicker = ticker
20912111

20922112
if (ticker && typeof ticker === 'object' && typeof ticker.unref === 'function') {
@@ -2193,12 +2213,12 @@ export default class GoTrueClient {
21932213

21942214
// session will expire in this many ticks (or has already expired if <= 0)
21952215
const expiresInTicks = Math.floor(
2196-
(session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION
2216+
(session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION_MS
21972217
)
21982218

21992219
this._debug(
22002220
'#_autoRefreshTokenTick()',
2201-
`access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`
2221+
`access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION_MS}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`
22022222
)
22032223

22042224
if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {

0 commit comments

Comments
 (0)