fix: queries not waiting for authentication
This commit is contained in:
committed by
liyasthomas
parent
6b8bc618dc
commit
2761894164
@@ -11,16 +11,24 @@ import {
|
||||
createClient,
|
||||
TypedDocumentNode,
|
||||
OperationResult,
|
||||
defaultExchanges,
|
||||
dedupExchange,
|
||||
OperationContext,
|
||||
cacheExchange,
|
||||
fetchExchange,
|
||||
makeOperation,
|
||||
} from "@urql/core"
|
||||
import { authExchange } from "@urql/exchange-auth"
|
||||
import { devtoolsExchange } from "@urql/devtools"
|
||||
import * as E from "fp-ts/Either"
|
||||
import * as TE from "fp-ts/TaskEither"
|
||||
import { pipe, constVoid } from "fp-ts/function"
|
||||
import { subscribe } from "wonka"
|
||||
import clone from "lodash/clone"
|
||||
import { getAuthIDToken } from "~/helpers/fb/auth"
|
||||
import {
|
||||
getAuthIDToken,
|
||||
probableUser$,
|
||||
waitProbableLoginToConfirm,
|
||||
} from "~/helpers/fb/auth"
|
||||
|
||||
const BACKEND_GQL_URL =
|
||||
process.env.CONTEXT === "production"
|
||||
@@ -29,16 +37,47 @@ const BACKEND_GQL_URL =
|
||||
|
||||
const client = createClient({
|
||||
url: BACKEND_GQL_URL,
|
||||
fetchOptions: () => {
|
||||
const token = getAuthIDToken()
|
||||
exchanges: [
|
||||
devtoolsExchange,
|
||||
dedupExchange,
|
||||
cacheExchange,
|
||||
authExchange({
|
||||
addAuthToOperation({ authState, operation }) {
|
||||
if (!authState || !authState.authToken) {
|
||||
return operation
|
||||
}
|
||||
|
||||
return {
|
||||
headers: {
|
||||
authorization: token ? `Bearer ${token}` : "",
|
||||
const fetchOptions =
|
||||
typeof operation.context.fetchOptions === "function"
|
||||
? operation.context.fetchOptions()
|
||||
: operation.context.fetchOptions || {}
|
||||
|
||||
return makeOperation(operation.kind, operation, {
|
||||
...operation.context,
|
||||
fetchOptions: {
|
||||
...fetchOptions,
|
||||
headers: {
|
||||
...fetchOptions.headers,
|
||||
Authorization: `Bearer ${authState.authToken}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
exchanges: [devtoolsExchange, ...defaultExchanges],
|
||||
willAuthError({ authState }) {
|
||||
return !authState || !authState.authToken
|
||||
},
|
||||
getAuth: async () => {
|
||||
if (!probableUser$.value) return { authToken: null }
|
||||
|
||||
await waitProbableLoginToConfirm()
|
||||
|
||||
return {
|
||||
authToken: getAuthIDToken(),
|
||||
}
|
||||
},
|
||||
}),
|
||||
fetchExchange,
|
||||
],
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -118,7 +157,7 @@ export function useGQLQuery<
|
||||
// Take the network error value
|
||||
result.error?.networkError,
|
||||
// If it null, set the left to the generic error name
|
||||
E.fromNullable(result.error?.name),
|
||||
E.fromNullable(result.error?.message),
|
||||
E.match(
|
||||
// The left case (network error was null)
|
||||
(gqlErr) =>
|
||||
|
||||
@@ -33,6 +33,11 @@ import {
|
||||
Subscription,
|
||||
} from "rxjs"
|
||||
import { onBeforeUnmount, onMounted } from "@nuxtjs/composition-api"
|
||||
import {
|
||||
setLocalConfig,
|
||||
getLocalConfig,
|
||||
removeLocalConfig,
|
||||
} from "~/newstore/localpersistence"
|
||||
|
||||
export type HoppUser = User & {
|
||||
provider?: string
|
||||
@@ -40,9 +45,10 @@ export type HoppUser = User & {
|
||||
}
|
||||
|
||||
type AuthEvents =
|
||||
| { event: "login"; user: HoppUser }
|
||||
| { event: "logout" }
|
||||
| { event: "authTokenUpdate"; user: HoppUser; newToken: string | null }
|
||||
| { event: "probable_login"; user: HoppUser } // We have previous login state, but the app is waiting for authentication
|
||||
| { event: "login"; user: HoppUser } // We are authenticated
|
||||
| { event: "logout" } // No authentication and we have no previous state
|
||||
| { event: "authTokenUpdate"; user: HoppUser; newToken: string | null } // Token has been updated
|
||||
|
||||
/**
|
||||
* A BehaviorSubject emitting the currently logged in user (or null if not logged in)
|
||||
@@ -58,6 +64,26 @@ export const authIdToken$ = new BehaviorSubject<string | null>(null)
|
||||
*/
|
||||
export const authEvents$ = new Subject<AuthEvents>()
|
||||
|
||||
/**
|
||||
* Like currentUser$ but also gives probable user value
|
||||
*/
|
||||
export const probableUser$ = new BehaviorSubject<HoppUser | null>(null)
|
||||
|
||||
/**
|
||||
* Resolves when the probable login resolves into proper login
|
||||
*/
|
||||
export const waitProbableLoginToConfirm = () =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
if (authIdToken$.value) resolve()
|
||||
|
||||
if (!probableUser$.value) reject(new Error("no_probable_user"))
|
||||
|
||||
const sub = authIdToken$.pipe(filter((token) => !!token)).subscribe(() => {
|
||||
sub?.unsubscribe()
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Initializes the firebase authentication related subjects
|
||||
*/
|
||||
@@ -67,6 +93,17 @@ export function initAuth() {
|
||||
|
||||
let extraSnapshotStop: (() => void) | null = null
|
||||
|
||||
probableUser$.next(JSON.parse(getLocalConfig("login_state") ?? "null"))
|
||||
|
||||
currentUser$.subscribe((user) => {
|
||||
if (user) {
|
||||
probableUser$.next(user)
|
||||
} else {
|
||||
probableUser$.next(null)
|
||||
removeLocalConfig("login_state")
|
||||
}
|
||||
})
|
||||
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
/** Whether the user was logged in before */
|
||||
const wasLoggedIn = currentUser$.value !== null
|
||||
@@ -135,6 +172,8 @@ export function initAuth() {
|
||||
newToken: authIdToken$.value,
|
||||
user: currentUser$.value!!, // Force not-null because user is defined
|
||||
})
|
||||
|
||||
setLocalConfig("login_state", JSON.stringify(user))
|
||||
} else {
|
||||
authIdToken$.next(null)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"@nuxtjs/sitemap": "^2.4.0",
|
||||
"@nuxtjs/toast": "^3.3.1",
|
||||
"@urql/core": "^2.3.3",
|
||||
"@urql/exchange-auth": "^0.1.6",
|
||||
"acorn": "^8.5.0",
|
||||
"acorn-walk": "^8.2.0",
|
||||
"axios": "^0.24.0",
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -49,6 +49,7 @@ importers:
|
||||
'@types/splitpanes': ^2.2.1
|
||||
'@urql/core': ^2.3.3
|
||||
'@urql/devtools': ^2.0.3
|
||||
'@urql/exchange-auth': ^0.1.6
|
||||
'@vue/runtime-dom': ^3.2.20
|
||||
'@vue/test-utils': ^1.2.2
|
||||
acorn: ^8.5.0
|
||||
@@ -123,6 +124,7 @@ importers:
|
||||
'@nuxtjs/sitemap': 2.4.0
|
||||
'@nuxtjs/toast': 3.3.1
|
||||
'@urql/core': 2.3.3_graphql@15.7.2
|
||||
'@urql/exchange-auth': 0.1.6_graphql@15.7.2
|
||||
acorn: 8.5.0
|
||||
acorn-walk: 8.2.0
|
||||
axios: 0.24.0
|
||||
@@ -4692,6 +4694,16 @@ packages:
|
||||
wonka: 4.0.15
|
||||
dev: true
|
||||
|
||||
/@urql/exchange-auth/0.1.6_graphql@15.7.2:
|
||||
resolution: {integrity: sha512-jVyUaV+hHe3p2rIJauh6lgILMAjXOsHQ98xjKhUF3TXYx88TZXuBIl5DPZwnMcGra8YPOSHO/Wsn6NEjO5hQ+Q==}
|
||||
peerDependencies:
|
||||
graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0
|
||||
dependencies:
|
||||
'@urql/core': 2.3.3_graphql@15.7.2
|
||||
graphql: 15.7.2
|
||||
wonka: 4.0.15
|
||||
dev: false
|
||||
|
||||
/@vue/babel-helper-vue-jsx-merge-props/1.2.1:
|
||||
resolution: {integrity: sha512-QOi5OW45e2R20VygMSNhyQHvpdUwQZqGPc748JLGCYEy+yp8fNFNdbNIGAgZmi9e+2JHPd6i6idRuqivyicIkA==}
|
||||
dev: false
|
||||
|
||||
Reference in New Issue
Block a user