@supabase/gotrue-js 2.119.0-canary.2 → 2.119.0-canary.4
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/README.md +10 -0
- package/dist/main/GoTrueClient.d.ts +33 -8
- package/dist/main/GoTrueClient.d.ts.map +1 -1
- package/dist/main/GoTrueClient.js +85 -22
- package/dist/main/GoTrueClient.js.map +1 -1
- package/dist/main/lib/types.d.ts +1 -1
- package/dist/main/lib/version.d.ts +1 -1
- package/dist/main/lib/version.js +1 -1
- package/dist/module/GoTrueClient.d.ts +33 -8
- package/dist/module/GoTrueClient.d.ts.map +1 -1
- package/dist/module/GoTrueClient.js +85 -22
- package/dist/module/GoTrueClient.js.map +1 -1
- package/dist/module/lib/types.d.ts +1 -1
- package/dist/module/lib/version.d.ts +1 -1
- package/dist/module/lib/version.js +1 -1
- package/dist/tsconfig.module.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/migrations/sign-out-global-keeps-session.md +41 -0
- package/package.json +1 -1
- package/src/GoTrueClient.ts +92 -30
- package/src/lib/types.ts +1 -1
- package/src/lib/version.ts +1 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# `signOut` keeps the session when the server logout fails
|
|
2
|
+
|
|
3
|
+
**Since:** the first release after v2.118.0
|
|
4
|
+
**Action required by:** none in v2.x. See [Planned for v3](#planned-for-v3).
|
|
5
|
+
|
|
6
|
+
`signOut({ scope: 'global' })` and `signOut({ scope: 'others' })` now report a server or network failure without touching the session they were asked to revoke. `signOut({ scope: 'local' })` always signs out this device.
|
|
7
|
+
|
|
8
|
+
## What changed
|
|
9
|
+
|
|
10
|
+
- **`global` (the default scope):** the local session is cleared, and `SIGNED_OUT` fires, only after the server accepts the logout. If the request fails, for example because the device is offline, the local session is kept, no `SIGNED_OUT` event fires and the error is returned.
|
|
11
|
+
- **`local`:** the local session is removed and `SIGNED_OUT` fires before the request to revoke the refresh token is sent, so signing out of this device never depends on the network. The request's error, if any, is still returned.
|
|
12
|
+
- **`others`:** the local session is never changed. A failed request returns the error.
|
|
13
|
+
|
|
14
|
+
Previously, a failed `global` sign out still cleared the local session. The refresh token then stayed valid on the server and the client no longer had the credentials to retry the revoke.
|
|
15
|
+
|
|
16
|
+
## Who is affected
|
|
17
|
+
|
|
18
|
+
**Callers that use the default scope** (`signOut()` or `signOut({ scope: 'global' })`) and assume it always ends the local session. If the device is offline or the auth server returns an error, `signOut()` now returns `{ error }` and the user stays signed in.
|
|
19
|
+
|
|
20
|
+
To always sign out this device, either use the `local` scope, or retry with it when `global` fails:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const { error } = await supabase.auth.signOut()
|
|
24
|
+
if (error) {
|
|
25
|
+
// The server could not revoke the sessions and the user is still signed in locally.
|
|
26
|
+
// Sign out this device regardless:
|
|
27
|
+
await supabase.auth.signOut({ scope: 'local' })
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
If you only want to sign out the current tab or device, pass `{ scope: 'local' }` directly. This is usually what a "Sign out" button wants.
|
|
32
|
+
|
|
33
|
+
## Responses that still count as a successful sign out
|
|
34
|
+
|
|
35
|
+
A `401`, `403` or `404` response from the auth server is treated as "already signed out": the local session is cleared and `error` is `null`. Auth servers from v2.145.0 answer `403` with `user_not_found` (and from v2.150.0 `session_not_found`) when the user or session no longer exists, and servers before v2.145.0 answered `401` or `404`.
|
|
36
|
+
|
|
37
|
+
The status is also `403` for `bad_jwt` and `user_banned`, where nothing was revoked, so these are treated as a successful sign out too. This is known, and changing it would be a breaking change.
|
|
38
|
+
|
|
39
|
+
## Planned for v3
|
|
40
|
+
|
|
41
|
+
v3 is expected to match on the error code instead of the status: only `session_not_found` and `user_not_found` will count as already signed out, and every other error will be returned with the session kept. Callers that handle the `error` from `signOut()` as shown above need no change.
|
package/package.json
CHANGED
package/src/GoTrueClient.ts
CHANGED
|
@@ -1107,7 +1107,7 @@ export default class GoTrueClient {
|
|
|
1107
1107
|
* Be aware that you may get back an error message that will not distinguish
|
|
1108
1108
|
* between the cases where the account does not exist or that the
|
|
1109
1109
|
* email/phone and password combination is wrong or that the account can only
|
|
1110
|
-
* be accessed via social
|
|
1110
|
+
* be accessed via social sign-in.
|
|
1111
1111
|
*
|
|
1112
1112
|
* @category Auth
|
|
1113
1113
|
*
|
|
@@ -1300,7 +1300,7 @@ export default class GoTrueClient {
|
|
|
1300
1300
|
* @category Auth
|
|
1301
1301
|
*
|
|
1302
1302
|
* @remarks
|
|
1303
|
-
* - This method is used for signing in using [
|
|
1303
|
+
* - This method is used for signing in using [social sign-in (OAuth) providers](/docs/guides/auth/social-sign-in).
|
|
1304
1304
|
* - It works by redirecting your application to the provider's authorization screen, before bringing back the user to your app.
|
|
1305
1305
|
*
|
|
1306
1306
|
* @example Sign in using a third-party provider
|
|
@@ -2219,7 +2219,7 @@ export default class GoTrueClient {
|
|
|
2219
2219
|
*
|
|
2220
2220
|
* Be aware that you may get back an error message that will not distinguish
|
|
2221
2221
|
* between the cases where the account does not exist or, that the account
|
|
2222
|
-
* can only be accessed via social
|
|
2222
|
+
* can only be accessed via social sign-in.
|
|
2223
2223
|
*
|
|
2224
2224
|
* Do note that you will need to configure a Whatsapp sender on Twilio
|
|
2225
2225
|
* if you are using phone sign in with the 'whatsapp' channel. The whatsapp
|
|
@@ -4026,12 +4026,31 @@ export default class GoTrueClient {
|
|
|
4026
4026
|
}
|
|
4027
4027
|
|
|
4028
4028
|
/**
|
|
4029
|
-
* Inside a browser context, `signOut()` will remove the logged in user from the browser session
|
|
4029
|
+
* Inside a browser context, `signOut()` will remove the logged in user from the browser session
|
|
4030
|
+
* and log them out - removing all items from localstorage and then trigger a `"SIGNED_OUT"`
|
|
4031
|
+
* event.
|
|
4030
4032
|
*
|
|
4031
|
-
* For server-side management, you can revoke all refresh tokens for a user by passing a user's
|
|
4032
|
-
*
|
|
4033
|
+
* For server-side management, you can revoke all refresh tokens for a user by passing a user's
|
|
4034
|
+
* JWT through to `auth.api.signOut(JWT: string)`.
|
|
4033
4035
|
*
|
|
4034
|
-
*
|
|
4036
|
+
* There is no way to revoke a user's access token jwt until it expires. It is recommended to set
|
|
4037
|
+
* a shorter expiry on the jwt for this reason.
|
|
4038
|
+
*
|
|
4039
|
+
* `signOut` behavior depends on the selected scope:
|
|
4040
|
+
*
|
|
4041
|
+
* - `local`: The local session is removed first and a `SIGNED_OUT` event is fired, then the client
|
|
4042
|
+
* makes a best-effort request to revoke the refresh token on the server. If the request fails,
|
|
4043
|
+
* the client is still signed out locally and the error is returned, but the server may retain
|
|
4044
|
+
* the refresh token until it expires.
|
|
4045
|
+
*
|
|
4046
|
+
* - `global`: All of the user's sessions are first invalidated on the server. Once the server
|
|
4047
|
+
* acknowledges the request, the local session is cleared. If server logout fails, the local
|
|
4048
|
+
* session is retained, no `SIGNED_OUT` event is triggered, and the server or network error is
|
|
4049
|
+
* returned. A 401, 403, or 404 response counts as a successful sign-out.
|
|
4050
|
+
*
|
|
4051
|
+
* - `others`: All other sessions are invalidated when the server accepts the request; the
|
|
4052
|
+
* current local session remains unchanged. In case of server or network failures, the error is
|
|
4053
|
+
* returned. A `SIGNED_OUT` event is never triggered, regardless of the outcome.
|
|
4035
4054
|
*
|
|
4036
4055
|
* **Warning:** the default `scope` is `'global'`. This signs the user out of
|
|
4037
4056
|
* **every device they are currently signed in on**, not just the current
|
|
@@ -4049,7 +4068,13 @@ export default class GoTrueClient {
|
|
|
4049
4068
|
* usually what apps want on a "Sign out" button, especially when users
|
|
4050
4069
|
* sign in from multiple devices and do not expect signing out of one to
|
|
4051
4070
|
* terminate the others.
|
|
4052
|
-
* -
|
|
4071
|
+
* - With the default `global` scope, `signOut()` while offline returns an error and keeps the user
|
|
4072
|
+
* signed in. To sign out regardless, fall back to `signOut({ scope: 'local' })` when the call
|
|
4073
|
+
* returns an error.
|
|
4074
|
+
* - Since Supabase Auth uses JWTs for authentication, the access token JWT will be valid until
|
|
4075
|
+
* it's expired. When the user signs out, Supabase revokes the refresh token and deletes the
|
|
4076
|
+
* JWT from the client-side. This does not revoke the JWT and it will still be valid until it
|
|
4077
|
+
* expires.
|
|
4053
4078
|
*
|
|
4054
4079
|
* @example Sign out of every device (global – default)
|
|
4055
4080
|
* ```js
|
|
@@ -4079,41 +4104,78 @@ export default class GoTrueClient {
|
|
|
4079
4104
|
return await this._signOut(options)
|
|
4080
4105
|
}
|
|
4081
4106
|
|
|
4082
|
-
protected async _signOut(
|
|
4083
|
-
|
|
4084
|
-
|
|
4107
|
+
protected async _signOut({ scope = 'global' }: SignOut = {}): Promise<{
|
|
4108
|
+
error: AuthError | null
|
|
4109
|
+
}> {
|
|
4085
4110
|
return await this._useSession(async (result) => {
|
|
4086
4111
|
const removeCurrentSession = async () => {
|
|
4087
4112
|
await this._removeSession()
|
|
4088
4113
|
}
|
|
4089
4114
|
const { data, error: sessionError } = result
|
|
4090
4115
|
if (sessionError && !isAuthSessionMissingError(sessionError)) {
|
|
4116
|
+
// A retryable refresh failure (offline, 5xx) leaves the session in storage and no access
|
|
4117
|
+
// token to revoke with, so `local` still has to sign out this device. A rejected refresh
|
|
4118
|
+
// has already removed the session and fired `SIGNED_OUT`.
|
|
4119
|
+
if (scope === 'local' && isAuthRetryableFetchError(sessionError)) {
|
|
4120
|
+
await removeCurrentSession()
|
|
4121
|
+
}
|
|
4091
4122
|
return this._returnResult({ error: sessionError })
|
|
4092
4123
|
}
|
|
4093
4124
|
const accessToken = data.session?.access_token
|
|
4125
|
+
let removeError: { thrown: unknown } | null = null
|
|
4126
|
+
const finish = <T>(value: T): T => {
|
|
4127
|
+
if (removeError) {
|
|
4128
|
+
throw removeError.thrown
|
|
4129
|
+
}
|
|
4130
|
+
return value
|
|
4131
|
+
}
|
|
4132
|
+
if (scope === 'local') {
|
|
4133
|
+
// The local session is removed before the request is sent, so signing out of this device
|
|
4134
|
+
// never depends on the network. The request only revokes the refresh token on the server.
|
|
4135
|
+
// A throwing `SIGNED_OUT` subscriber must not skip that request, so its error is rethrown
|
|
4136
|
+
// once the request has been attempted.
|
|
4137
|
+
try {
|
|
4138
|
+
await removeCurrentSession()
|
|
4139
|
+
} catch (thrown) {
|
|
4140
|
+
removeError = { thrown }
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4094
4143
|
if (accessToken) {
|
|
4095
4144
|
const { error } = await this.admin.signOut(accessToken, scope)
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4145
|
+
// The current behavior is wrong for the current version of auth (v2.197.0). However,
|
|
4146
|
+
// changing it could be considered a breaking change and must wait for v3. Before modifying
|
|
4147
|
+
// this code we also need to consider which range of Auth versions we aim to support. Right
|
|
4148
|
+
// now this is not defined and thus undefined.
|
|
4149
|
+
//
|
|
4150
|
+
// Some constraints to be aware of:
|
|
4151
|
+
// - Since supabase/auth v2.145.0 a missing user answers 403 `user_not_found`.
|
|
4152
|
+
// - Since supabase/auth v2.150.0 a missing session answers 403 `session_not_found`.
|
|
4153
|
+
// - Before v2.145.0 an invalid JWT answered 401.
|
|
4154
|
+
// - Before v2.145.0 a missing user answers 404., so those statuses are treated the same way.
|
|
4155
|
+
//
|
|
4156
|
+
// TODO(v3): if we only support auth v2.150.0 or later, change this to:
|
|
4157
|
+
// - Match on `error.code` instead of `error.status`, and treat only 403 `session_not_found`
|
|
4158
|
+
// and `user_not_found` as already signed out. Drop the 401 and 404 handling.
|
|
4159
|
+
// - Return every other error, e.g. 403 `bad_jwt` or `user_banned`, or a 401 or 404 from a
|
|
4160
|
+
// gateway, instead of treating it as signed out. Today nothing was revoked, yet the
|
|
4161
|
+
// session is cleared and `error` is null. With `global` the session is then kept.
|
|
4162
|
+
// - For `others`, return `session_not_found` as well. It describes the current session, not
|
|
4163
|
+
// the sessions being revoked.
|
|
4164
|
+
if (
|
|
4165
|
+
error &&
|
|
4166
|
+
!(
|
|
4167
|
+
(isAuthApiError(error) &&
|
|
4168
|
+
(error.status === 404 || error.status === 401 || error.status === 403)) ||
|
|
4169
|
+
isAuthSessionMissingError(error)
|
|
4170
|
+
)
|
|
4171
|
+
) {
|
|
4172
|
+
return finish(this._returnResult({ error }))
|
|
4111
4173
|
}
|
|
4112
4174
|
}
|
|
4113
|
-
if (scope
|
|
4175
|
+
if (scope === 'global') {
|
|
4114
4176
|
await removeCurrentSession()
|
|
4115
4177
|
}
|
|
4116
|
-
return this._returnResult({ error: null })
|
|
4178
|
+
return finish(this._returnResult({ error: null }))
|
|
4117
4179
|
})
|
|
4118
4180
|
}
|
|
4119
4181
|
|
|
@@ -4231,9 +4293,9 @@ export default class GoTrueClient {
|
|
|
4231
4293
|
* ```
|
|
4232
4294
|
*
|
|
4233
4295
|
* @exampleDescription Store OAuth provider tokens on sign in
|
|
4234
|
-
* When using [OAuth (
|
|
4296
|
+
* When using [OAuth (social sign-in)](/docs/guides/auth/social-sign-in) you sometimes wish to get access to the provider's access token and refresh token, in order to call provider APIs in the name of the user.
|
|
4235
4297
|
*
|
|
4236
|
-
* For example, if you are using [Sign in with Google](/docs/guides/auth/social-
|
|
4298
|
+
* For example, if you are using [Sign in with Google](/docs/guides/auth/social-sign-in/auth-google) you may want to use the provider token to call Google APIs on behalf of the user. Supabase Auth does not keep track of the provider access and refresh token, but does return them for you once, immediately after sign in. You can use the `onAuthStateChange` method to listen for the presence of the provider tokens and store them in local storage. You can further send them to your server's APIs for use on the backend.
|
|
4237
4299
|
*
|
|
4238
4300
|
* Finally, make sure you remove them from local storage on the `SIGNED_OUT` event. If the OAuth provider supports token revocation, make sure you call those APIs either from the frontend or schedule them to be called on the backend.
|
|
4239
4301
|
*
|
package/src/lib/types.ts
CHANGED
|
@@ -2032,7 +2032,7 @@ export interface GoTrueMFAApi {
|
|
|
2032
2032
|
* Returns the Authenticator Assurance Level (AAL) for the active session.
|
|
2033
2033
|
*
|
|
2034
2034
|
* - `aal1` (or `null`) means that the user's identity has been verified only
|
|
2035
|
-
* with a conventional login (email+password, OTP, magic link, social
|
|
2035
|
+
* with a conventional login (email+password, OTP, magic link, social sign-in,
|
|
2036
2036
|
* etc.).
|
|
2037
2037
|
* - `aal2` means that the user's identity has been verified both with a conventional login and at least one MFA factor.
|
|
2038
2038
|
*
|
package/src/lib/version.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
// - Debugging and support (identifying which version is running)
|
|
5
5
|
// - Telemetry and logging (version reporting in errors/analytics)
|
|
6
6
|
// - Ensuring build artifacts match the published package version
|
|
7
|
-
export const version = '2.119.0-canary.
|
|
7
|
+
export const version = '2.119.0-canary.4'
|