saleor: refine the auth process

This commit is contained in:
Zaiste 2021-05-11 14:27:30 +02:00
parent 85d203e85c
commit e98a9f4d6f
No known key found for this signature in database
GPG Key ID: 15DF7EBC7F2FFE35
12 changed files with 78 additions and 101 deletions

View File

@ -45,8 +45,8 @@ export class Config {
const config = new Config({ const config = new Config({
locale: 'en-US', locale: 'en-US',
commerceUrl: API_URL, commerceUrl: API_URL,
apiToken: "", apiToken: "saleor.Token",
cartCookie: "saleorCheckoutID", cartCookie: "saleor.CheckoutID",
cartCookieMaxAge: 60 * 60 * 24 * 30, cartCookieMaxAge: 60 * 60 * 24 * 30,
fetch: fetchGraphqlApi, fetch: fetchGraphqlApi,
customerCookie: "", customerCookie: "",

View File

@ -4,6 +4,7 @@ import fetch from './fetch'
import { API_URL } from '../../const' import { API_URL } from '../../const'
import { getError } from '../../utils/handle-fetch-response' import { getError } from '../../utils/handle-fetch-response'
import { getConfig } from '..' import { getConfig } from '..'
import { getToken } from '@framework/utils'
const fetchGraphqlApi: GraphQLFetcher = async ( const fetchGraphqlApi: GraphQLFetcher = async (
query: string, query: string,
@ -12,11 +13,13 @@ const fetchGraphqlApi: GraphQLFetcher = async (
) => { ) => {
// FIXME @zaiste follow the bigcommerce example // FIXME @zaiste follow the bigcommerce example
const config = getConfig() const config = getConfig()
const token = getToken();
const res = await fetch(API_URL || '', { const res = await fetch(API_URL || '', {
...fetchOptions, ...fetchOptions,
method: 'POST', method: 'POST',
headers: { headers: {
Authorization: `Bearer ${token}`,
...fetchOptions?.headers, ...fetchOptions?.headers,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },

View File

@ -1,31 +1,20 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types' import type { MutationHook } from '@commerce/utils/types'
import { CommerceError, ValidationError } from '@commerce/utils/errors' import { CommerceError } from '@commerce/utils/errors'
import useCustomer from '../customer/use-customer' import useCustomer from '../customer/use-customer'
import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' import tokenCreateMutation from '../utils/mutations/customer-access-token-create'
import { import {
CustomerAccessTokenCreateInput,
CustomerUserError,
Mutation, Mutation,
MutationCheckoutCreateArgs, MutationTokenCreateArgs,
} from '../schema' } from '../schema'
import useLogin, { UseLogin } from '@commerce/auth/use-login' import useLogin, { UseLogin } from '@commerce/auth/use-login'
import { setCustomerToken, throwUserErrors } from '../utils' import { setCSRFToken, setToken, throwUserErrors } from '../utils'
export default useLogin as UseLogin<typeof handler> export default useLogin as UseLogin<typeof handler>
const getErrorMessage = ({ code, message }: CustomerUserError) => { export const handler: MutationHook<null, {}, MutationTokenCreateArgs> = {
switch (code) {
case 'UNIDENTIFIED_CUSTOMER':
message = 'Cannot find an account that matches the provided credentials'
break
}
return message
}
export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
fetchOptions: { fetchOptions: {
query: createCustomerAccessTokenMutation, query: tokenCreateMutation,
}, },
async fetcher({ input: { email, password }, options, fetch }) { async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) { if (!(email && password)) {
@ -35,23 +24,21 @@ export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
}) })
} }
const { customerAccessTokenCreate } = await fetch< const { tokenCreate } = await fetch<
Mutation, Mutation,
MutationCheckoutCreateArgs MutationTokenCreateArgs
>({ >({
...options, ...options,
variables: { variables: { email, password },
input: { email, password },
},
}) })
throwUserErrors(customerAccessTokenCreate?.customerUserErrors) throwUserErrors(tokenCreate?.errors)
const customerAccessToken = customerAccessTokenCreate?.customerAccessToken const { token, csrfToken } = tokenCreate!;
const accessToken = customerAccessToken?.accessToken
if (accessToken) { if (token && csrfToken) {
setCustomerToken(accessToken) setToken(token)
setCSRFToken(csrfToken)
} }
return null return null

View File

@ -3,7 +3,7 @@ import type { MutationHook } from '@commerce/utils/types'
import useLogout, { UseLogout } from '@commerce/auth/use-logout' import useLogout, { UseLogout } from '@commerce/auth/use-logout'
import useCustomer from '../customer/use-customer' import useCustomer from '../customer/use-customer'
import customerAccessTokenDeleteMutation from '../utils/mutations/customer-access-token-delete' import customerAccessTokenDeleteMutation from '../utils/mutations/customer-access-token-delete'
import { getCustomerToken, setCustomerToken } from '../utils/customer-token' import { setToken } from '../utils/customer-token'
export default useLogout as UseLogout<typeof handler> export default useLogout as UseLogout<typeof handler>
@ -14,11 +14,9 @@ export const handler: MutationHook<null> = {
async fetcher({ options, fetch }) { async fetcher({ options, fetch }) {
await fetch({ await fetch({
...options, ...options,
variables: { variables: {},
customerAccessToken: getCustomerToken(),
},
}) })
setCustomerToken(null) setToken()
return null return null
}, },
useHook: ({ fetch }) => () => { useHook: ({ fetch }) => () => {

View File

@ -1,12 +1,12 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types' import type { MutationHook } from '@commerce/utils/types'
import { CommerceError, ValidationError } from '@commerce/utils/errors' import { CommerceError } from '@commerce/utils/errors'
import useSignup, { UseSignup } from '@commerce/auth/use-signup' import useSignup, { UseSignup } from '@commerce/auth/use-signup'
import useCustomer from '../customer/use-customer' import useCustomer from '../customer/use-customer'
import { import {
CustomerCreate, AccountRegisterInput,
Mutation, Mutation,
MutationCustomerCreateArgs, MutationAccountRegisterArgs
} from '../schema' } from '../schema'
import { customerCreateMutation } from '../utils/mutations' import { customerCreateMutation } from '../utils/mutations'
@ -17,27 +17,18 @@ export default useSignup as UseSignup<typeof handler>
export const handler: MutationHook< export const handler: MutationHook<
null, null,
{}, {},
CustomerCreate, AccountRegisterInput,
CustomerCreate AccountRegisterInput
> = { > = {
fetchOptions: { fetchOptions: {
query: customerCreateMutation, query: customerCreateMutation,
}, },
async fetcher({ async fetcher({
input: { user }, input: { email, password },
options, options,
fetch, fetch,
}) { }) {
if (!user) { if (!(email && password)) {
throw new CommerceError({
message:
'A first name, last name, email and password are required to signup',
})
}
const { firstName, lastName, email, password } = user;
if (!(firstName && lastName && email && password)) {
throw new CommerceError({ throw new CommerceError({
message: message:
'A first name, last name, email and password are required to signup', 'A first name, last name, email and password are required to signup',
@ -46,13 +37,11 @@ export const handler: MutationHook<
const { customerCreate } = await fetch< const { customerCreate } = await fetch<
Mutation, Mutation,
MutationCustomerCreateArgs MutationAccountRegisterArgs
>({ >({
...options, ...options,
variables: { variables: {
input: { input: {
firstName,
lastName,
email, email,
password, password,
}, },

View File

@ -1,7 +1,7 @@
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
import { Customer } from '@commerce/types' import { Customer } from '@commerce/types'
import { SWRHook } from '@commerce/utils/types' import { SWRHook } from '@commerce/utils/types'
import { getCustomerQuery, getCustomerToken } from '../utils' import { getCustomerQuery, getCSRFToken } from '../utils'
export default useCustomer as UseCustomer<typeof handler> export default useCustomer as UseCustomer<typeof handler>
@ -12,7 +12,7 @@ export const handler: SWRHook<Customer | null> = {
async fetcher({ options, fetch }) { async fetcher({ options, fetch }) {
const data = await fetch<any | null>({ const data = await fetch<any | null>({
...options, ...options,
variables: { customerAccessToken: getCustomerToken() }, variables: { customerAccessToken: getCSRFToken() },
}) })
return data.customer ?? null return data.customer ?? null
}, },

View File

@ -1,6 +1,6 @@
import { Fetcher } from '@commerce/utils/types' import { Fetcher } from '@commerce/utils/types'
import { API_URL } from './const' import { API_URL } from './const'
import { handleFetchResponse } from './utils' import { getToken, handleFetchResponse } from './utils'
const fetcher: Fetcher = async ({ const fetcher: Fetcher = async ({
url = API_URL, url = API_URL,
@ -8,11 +8,14 @@ const fetcher: Fetcher = async ({
variables, variables,
query, query,
}) => { }) => {
const token = getToken();
return handleFetchResponse( return handleFetchResponse(
await fetch(url!, { await fetch(url!, {
method, method,
body: JSON.stringify({ query, variables }), body: JSON.stringify({ query, variables }),
headers: { headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}) })

View File

@ -1,4 +1,3 @@
import { Product } from '@commerce/types'
import { getConfig, SaleorConfig } from '../api' import { getConfig, SaleorConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products' import fetchAllProducts from '../api/utils/fetch-all-products'
import { ProductCountableEdge } from '../schema' import { ProductCountableEdge } from '../schema'

View File

@ -1,20 +1,19 @@
import Cookies, { CookieAttributes } from 'js-cookie' import Cookies, { CookieAttributes } from 'js-cookie'
export const getCustomerToken = () => Cookies.get('saleorAccessToken') export const getToken = () => Cookies.get('saleor.Token')
export const setToken = (token?: string, options?: CookieAttributes) => {
setCookie('saleor.Token', token, options)
}
export const setCustomerToken = ( export const getCSRFToken = () => Cookies.get('saleor.CSRFToken')
token: string | null, export const setCSRFToken = (token?: string, options?: CookieAttributes) => {
options?: CookieAttributes setCookie('saleor.CSRFToken', token, options)
) => { }
const setCookie = (name: string, token?: string, options?: CookieAttributes) => {
if (!token) { if (!token) {
Cookies.remove('saleorAccessToken') Cookies.remove(name)
} else { } else {
Cookies.set( Cookies.set(name, token, options ?? { expires: 60 * 60 * 24 * 30 })
'saleorAccessToken',
token,
options ?? {
expires: 60 * 60 * 24 * 30,
}
)
} }
} }

View File

@ -1,36 +1,38 @@
import { FetcherOptions } from '@commerce/utils/types' import { FetcherOptions } from '@commerce/utils/types'
import { CustomerAccessTokenCreateInput } from '../schema' import { CreateToken, Mutation, MutationTokenCreateArgs } from '../schema'
import { setCustomerToken } from './customer-token' import { setToken, setCSRFToken } from './customer-token'
import { customerAccessTokenCreateMutation } from './mutations' import { customerAccessTokenCreateMutation } from './mutations'
import throwUserErrors from './throw-user-errors' import throwUserErrors from './throw-user-errors'
const handleLogin = (data: any) => { const handleLogin = (data: CreateToken) => {
const response = data.customerAccessTokenCreate throwUserErrors(data?.errors)
throwUserErrors(response?.customerUserErrors)
const customerAccessToken = response?.customerAccessToken const token = data?.token
const accessToken = customerAccessToken?.accessToken
if (accessToken) { if (token) {
setCustomerToken(accessToken) setToken(token)
setCSRFToken(token)
} }
return customerAccessToken return token
} }
export const handleAutomaticLogin = async ( export const handleAutomaticLogin = async (
fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>, fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>,
input: CustomerAccessTokenCreateInput input: MutationTokenCreateArgs
) => { ) => {
try { try {
const loginData = await fetch({ const { tokenCreate } = await fetch<
Mutation,
MutationTokenCreateArgs
>({
query: customerAccessTokenCreateMutation, query: customerAccessTokenCreateMutation,
variables: { variables: { ...input },
input,
},
}) })
handleLogin(loginData) handleLogin(tokenCreate!)
} catch (error) {} } catch (error) {
//
}
} }
export default handleLogin export default handleLogin

View File

@ -1,11 +1,10 @@
const customerAccessTokenCreateMutation = /* GraphQL */ ` const tokenCreateMutation = /* GraphQL */ `
mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) { mutation tokenCreate($email: String!, $password: String!) {
customerAccessTokenCreate(input: $input) { tokenCreate(email: $email, password: $password) {
customerAccessToken { token
accessToken refreshToken
expiresAt csrfToken
} errors {
customerUserErrors {
code code
field field
message message
@ -13,4 +12,4 @@ const customerAccessTokenCreateMutation = /* GraphQL */ `
} }
} }
` `
export default customerAccessTokenCreateMutation export default tokenCreateMutation;

View File

@ -1,9 +1,7 @@
const customerAccessTokenDeleteMutation = /* GraphQL */ ` const customerAccessTokenDeleteMutation = /* GraphQL */ `
mutation customerAccessTokenDelete($customerAccessToken: String!) { mutation customerAccessTokenDelete {
customerAccessTokenDelete(customerAccessToken: $customerAccessToken) { tokensDeactivateAll {
deletedAccessToken errors {
deletedCustomerAccessTokenId
userErrors {
field field
message message
} }