diff --git a/src/components/App.tsx b/src/components/App.tsx
index 4b39c741..18952224 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -4,7 +4,6 @@ import { Meta, MetaProvider } from '@solidjs/meta'
import { Component, createEffect, createMemo } from 'solid-js'
import { Dynamic } from 'solid-js/web'
-import { AuthorizerProvider } from '../context/authorizer'
import { ConfirmProvider } from '../context/confirm'
import { ConnectProvider } from '../context/connect'
import { EditorProvider } from '../context/editor'
@@ -120,17 +119,15 @@ export const App = (props: Props) => {
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Nav/AuthModal/EmailConfirm.tsx b/src/components/Nav/AuthModal/EmailConfirm.tsx
index 09b4bf1b..3038fb73 100644
--- a/src/components/Nav/AuthModal/EmailConfirm.tsx
+++ b/src/components/Nav/AuthModal/EmailConfirm.tsx
@@ -3,7 +3,6 @@ import type { ConfirmEmailSearchParams } from './types'
import { clsx } from 'clsx'
import { createMemo, createSignal, onMount, Show } from 'solid-js'
-import { useAuthorizer } from '../../../context/authorizer'
import { useLocalize } from '../../../context/localize'
import { useSession } from '../../../context/session'
import { ApiError } from '../../../graphql/error'
@@ -17,7 +16,7 @@ export const EmailConfirm = () => {
const {
actions: { confirmEmail },
} = useSession()
- const [{ user }] = useAuthorizer()
+ const { user } = useSession()
const [isTokenExpired, setIsTokenExpired] = createSignal(false)
const [isTokenInvalid, setIsTokenInvalid] = createSignal(false)
diff --git a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx
index ff2b5176..ea7e48f0 100644
--- a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx
+++ b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx
@@ -3,7 +3,6 @@ import type { AuthModalSearchParams } from './types'
import { clsx } from 'clsx'
import { createSignal, JSX, Show } from 'solid-js'
-import { useAuthorizer } from '../../../context/authorizer'
import { useLocalize } from '../../../context/localize'
import { ApiError } from '../../../graphql/error'
import { useRouter } from '../../../stores/router'
@@ -12,6 +11,7 @@ import { validateEmail } from '../../../utils/validateEmail'
import { email, setEmail } from './sharedLogic'
import styles from './AuthModal.module.scss'
+import { useSession } from '../../../context/session'
type FormFields = {
email: string
@@ -26,7 +26,9 @@ export const ForgotPasswordForm = () => {
setValidationErrors(({ email: _notNeeded, ...rest }) => rest)
setEmail(newEmail)
}
- const [, { authorizer }] = useAuthorizer()
+ const {
+ actions: { authorizer },
+ } = useSession()
const [submitError, setSubmitError] = createSignal('')
const [isSubmitting, setIsSubmitting] = createSignal(false)
const [validationErrors, setValidationErrors] = createSignal({})
diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx
index a7e4f994..851045e5 100644
--- a/src/components/Nav/AuthModal/LoginForm.tsx
+++ b/src/components/Nav/AuthModal/LoginForm.tsx
@@ -3,7 +3,6 @@ import type { AuthModalSearchParams } from './types'
import { clsx } from 'clsx'
import { createSignal, Show } from 'solid-js'
-import { useAuthorizer } from '../../../context/authorizer'
import { useLocalize } from '../../../context/localize'
import { useSession } from '../../../context/session'
import { useSnackbar } from '../../../context/snackbar'
@@ -67,8 +66,10 @@ export const LoginForm = () => {
setIsLinkSent(true)
setIsEmailNotConfirmed(false)
setSubmitError('')
- const [{ token }, { authorizer }] = useAuthorizer()
- const result = await authorizer().verifyEmail({ token: token.id_token })
+ const {
+ actions: { authorizer, getToken },
+ } = useSession()
+ const result = await authorizer().verifyEmail({ token: getToken() })
if (!result) setSubmitError('cant sign send link') // TODO:
}
diff --git a/src/components/Nav/AuthModal/RegisterForm.tsx b/src/components/Nav/AuthModal/RegisterForm.tsx
index fc797367..b83c3190 100644
--- a/src/components/Nav/AuthModal/RegisterForm.tsx
+++ b/src/components/Nav/AuthModal/RegisterForm.tsx
@@ -4,7 +4,6 @@ import type { JSX } from 'solid-js'
import { clsx } from 'clsx'
import { Show, createSignal } from 'solid-js'
-import { useAuthorizer } from '../../../context/authorizer'
import { useLocalize } from '../../../context/localize'
import { ApiError } from '../../../graphql/error'
import { checkEmail, useEmailChecks } from '../../../stores/emailChecks'
@@ -18,6 +17,7 @@ import { email, setEmail } from './sharedLogic'
import { SocialProviders } from './SocialProviders'
import styles from './AuthModal.module.scss'
+import { useSession } from '../../../context/session'
type FormFields = {
fullName: string
@@ -35,7 +35,9 @@ export const RegisterForm = () => {
const { changeSearchParam } = useRouter()
const { t } = useLocalize()
const { emailChecks } = useEmailChecks()
- const [, { authorizer }] = useAuthorizer()
+ const {
+ actions: { authorizer },
+ } = useSession()
const [submitError, setSubmitError] = createSignal('')
const [fullName, setFullName] = createSignal('')
const [password, setPassword] = createSignal('')
diff --git a/src/components/ProfileSettings/ProfileSettings.tsx b/src/components/ProfileSettings/ProfileSettings.tsx
index 480f1938..d5026dab 100644
--- a/src/components/ProfileSettings/ProfileSettings.tsx
+++ b/src/components/ProfileSettings/ProfileSettings.tsx
@@ -4,7 +4,7 @@ import deepEqual from 'fast-deep-equal'
import { createEffect, createSignal, For, lazy, Match, onCleanup, onMount, Show, Switch } from 'solid-js'
import { createStore } from 'solid-js/store'
-import { useAuthorizer } from '../../context/authorizer'
+import { useSession } from '../../context/session'
import { useConfirm } from '../../context/confirm'
import { useLocalize } from '../../context/localize'
import { useProfileForm } from '../../context/profile'
@@ -49,7 +49,9 @@ export const ProfileSettings = () => {
actions: { showSnackbar },
} = useSnackbar()
- const [, { setUser, authorizer }] = useAuthorizer()
+ const {
+ actions: { setUser, authorizer },
+ } = useSession()
const {
actions: { showConfirm },
diff --git a/src/context/authorizer.tsx b/src/context/authorizer.tsx
deleted file mode 100644
index 705a245e..00000000
--- a/src/context/authorizer.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import type { ParentComponent } from 'solid-js'
-
-import { Authorizer, User, AuthToken, ConfigType } from '@authorizerdev/authorizer-js'
-import { createContext, createEffect, createMemo, onMount, useContext } from 'solid-js'
-import { createStore } from 'solid-js/store'
-
-export type AuthorizerState = {
- user: User | null
- token: AuthToken | null
- loading: boolean
- config: ConfigType
-}
-
-type AuthorizerContextActions = {
- setLoading: (loading: boolean) => void
- setToken: (token: AuthToken | null) => void
- setUser: (user: User | null) => void
- setAuthData: (data: AuthorizerState) => void
- authorizer: () => Authorizer
- logout: () => Promise
-}
-const config: ConfigType = {
- authorizerURL: 'https://auth.discours.io',
- redirectURL: 'https://discoursio-webapp.vercel.app/?modal=auth',
- clientID: '9c113377-5eea-4c89-98e1-69302462fc08', // FIXME: use env?
-}
-
-const AuthorizerContext = createContext<[AuthorizerState, AuthorizerContextActions]>([
- {
- config,
- user: null,
- token: null,
- loading: false,
- },
- {
- setLoading: () => {},
- setToken: () => {},
- setUser: () => {},
- setAuthData: () => {},
- authorizer: () => new Authorizer(config),
- logout: async () => {},
- },
-])
-
-type AuthorizerProviderProps = {
- onStateChangeCallback?: (stateData: AuthorizerState) => void
-}
-
-export const AuthorizerProvider: ParentComponent = (props) => {
- const [state, setState] = createStore({
- user: null,
- token: null,
- loading: true,
- config,
- })
- const authorizer = createMemo(
- () =>
- new Authorizer({
- authorizerURL: state.config.authorizerURL,
- redirectURL: state.config.redirectURL,
- clientID: state.config.clientID,
- }),
- )
-
- createEffect(() => {
- if (props.onStateChangeCallback) {
- props.onStateChangeCallback(state)
- }
- })
-
- // Actions
- const setLoading = (loading: boolean) => {
- setState('loading', loading)
- }
-
- const handleTokenChange = (token: AuthToken | null) => {
- setState('token', token)
- }
-
- const setUser = (user: User | null) => {
- setState('user', user)
- }
-
- const setAuthData = (data: AuthorizerState) => {
- setState(data)
- }
-
- const logout = async () => {
- setState('loading', true)
- setState('user', null)
- }
-
- const interval: number | null = null
-
- const getToken = async () => {
- setState('loading', true)
- const metaRes = await authorizer().getMetaData()
- setState('config', (cfg) => ({ ...cfg, ...metaRes }))
- setState('loading', false)
- }
-
- onMount(() => {
- setState('config', { ...config, redirectURL: window.location.origin + '/?modal=auth' })
- })
-
- return (
-
- {props.children}
-
- )
-}
-
-export const useAuthorizer = () => useContext(AuthorizerContext)
diff --git a/src/context/connect.tsx b/src/context/connect.tsx
index 3bb1828a..311032d4 100644
--- a/src/context/connect.tsx
+++ b/src/context/connect.tsx
@@ -3,7 +3,6 @@ import type { Accessor, JSX } from 'solid-js'
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { createContext, useContext, createSignal, createEffect } from 'solid-js'
-import { useAuthorizer } from './authorizer'
import { useSession } from './session'
export interface SSEMessage {
diff --git a/src/context/editor.tsx b/src/context/editor.tsx
index fe07fee1..737026da 100644
--- a/src/context/editor.tsx
+++ b/src/context/editor.tsx
@@ -11,8 +11,8 @@ import { router, useRouter } from '../stores/router'
import { slugify } from '../utils/slugify'
import { useLocalize } from './localize'
-import { useSnackbar } from './snackbar'
import { useSession } from './session'
+import { useSnackbar } from './snackbar'
type WordCounter = {
characters: number
diff --git a/src/context/session.tsx b/src/context/session.tsx
index f79770ea..3ae1dab1 100644
--- a/src/context/session.tsx
+++ b/src/context/session.tsx
@@ -2,22 +2,44 @@ import type { AuthModalSource } from '../components/Nav/AuthModal/types'
import type { Author, Result } from '../graphql/schema/core.gen'
import type { Accessor, JSX, Resource } from 'solid-js'
-import { VerifyEmailInput, LoginInput, AuthToken, User } from '@authorizerdev/authorizer-js'
-import { createContext, createMemo, createResource, createSignal, onMount, useContext } from 'solid-js'
+import {
+ VerifyEmailInput,
+ LoginInput,
+ AuthToken,
+ User,
+ Authorizer,
+ ConfigType,
+} from '@authorizerdev/authorizer-js'
+import {
+ createContext,
+ createEffect,
+ createMemo,
+ createResource,
+ createSignal,
+ onMount,
+ useContext,
+} from 'solid-js'
import { apiClient } from '../graphql/client/core'
import { showModal } from '../stores/ui'
-
-import { useAuthorizer } from './authorizer'
import { useLocalize } from './localize'
import { useSnackbar } from './snackbar'
+const config: ConfigType = {
+ authorizerURL: 'https://auth.discours.io',
+ redirectURL: 'https://discoursio-webapp.vercel.app/?modal=auth',
+ clientID: '9c113377-5eea-4c89-98e1-69302462fc08', // FIXME: use env?
+}
+
export type SessionContextType = {
+ user: User | null
+ config: ConfigType
session: Resource
isSessionLoaded: Accessor
subscriptions: Accessor
author: Resource
isAuthenticated: Accessor
+ isAuthWithCallback: Accessor<() => void>
actions: {
getToken: () => string
loadSession: () => AuthToken | Promise
@@ -29,6 +51,10 @@ export type SessionContextType = {
signIn: (params: LoginInput) => Promise
signOut: () => Promise
confirmEmail: (input: VerifyEmailInput) => Promise
+ setIsSessionLoaded: (loaded: boolean) => void
+ setToken: (token: AuthToken | null) => void // setSession
+ setUser: (user: User | null) => void
+ authorizer: () => Authorizer
}
}
@@ -43,15 +69,18 @@ const EMPTY_SUBSCRIPTIONS = {
authors: [],
}
-export const SessionProvider = (props: { children: JSX.Element }) => {
+export const SessionProvider = (props: {
+ onStateChangeCallback(state: any): unknown
+ children: JSX.Element
+}) => {
const [isSessionLoaded, setIsSessionLoaded] = createSignal(false)
const [subscriptions, setSubscriptions] = createSignal(EMPTY_SUBSCRIPTIONS)
const { t } = useLocalize()
const {
actions: { showSnackbar },
} = useSnackbar()
- const [{ token }, { setUser, setToken, authorizer }] = useAuthorizer()
- const getToken = () => token.access_token
+ const [token, setToken] = createSignal()
+ const [user, setUser] = createSignal()
const loadSubscriptions = async (): Promise => {
const result = await apiClient.getMySubscriptions()
if (result) {
@@ -63,17 +92,16 @@ export const SessionProvider = (props: { children: JSX.Element }) => {
const getSession = async (): Promise => {
try {
- if (token) {
- const authResult = await authorizer().getSession()
- if (authResult && authResult.access_token) {
- console.log(authResult)
- setToken(authResult)
- if (authResult.user) setUser(authResult.user)
- loadSubscriptions()
- return authResult
- }
+ const authResult = await authorizer().getSession({
+ Authorization: getToken(),
+ })
+ if (authResult?.access_token) {
+ console.log(authResult)
+ setToken(authResult)
+ if (authResult.user) setUser(authResult.user)
+ loadSubscriptions()
+ return authResult
}
- return null
} catch (error) {
console.error('getSession error:', error)
setToken(null)
@@ -120,8 +148,39 @@ export const SessionProvider = (props: { children: JSX.Element }) => {
}
}
- const [isAuthWithCallback, setIsAuthWithCallback] = createSignal(null)
+ const authorizer = createMemo(
+ () =>
+ new Authorizer({
+ authorizerURL: config.authorizerURL,
+ redirectURL: config.redirectURL,
+ clientID: config.clientID,
+ }),
+ )
+ createEffect(() => {
+ if (props.onStateChangeCallback) {
+ props.onStateChangeCallback(token())
+ }
+ })
+
+ const [configuration, setConfig] = createSignal(config)
+
+ onMount(async () => {
+ setIsSessionLoaded(false)
+ console.log('[context.session] loading...')
+ const metaRes = await authorizer().getMetaData()
+ setConfig({ ...config, ...metaRes, redirectURL: window.location.origin + '/?modal=auth' })
+ console.log('[context.session] refreshing session...')
+ const s = await getSession()
+ console.log(`[context.session] ${s}`)
+ setToken(s)
+ console.log('[context.session] loading author...')
+ await loadAuthor()
+ setIsSessionLoaded(true)
+ console.log('[context.session] loaded')
+ })
+
+ const [isAuthWithCallback, setIsAuthWithCallback] = createSignal<() => void>()
const requireAuthentication = async (callback: () => void, modalSource: AuthModalSource) => {
setIsAuthWithCallback(() => callback)
@@ -132,12 +191,6 @@ export const SessionProvider = (props: { children: JSX.Element }) => {
}
}
- onMount(async () => {
- // Load the session and author data on mount
- await loadSession()
- loadAuthor()
- })
-
const signOut = async () => {
await authorizer().logout()
mutate(null)
@@ -155,26 +208,32 @@ export const SessionProvider = (props: { children: JSX.Element }) => {
}
}
+ const getToken = createMemo(() => token()?.access_token)
+
const actions = {
+ getToken,
loadSession,
+ loadSubscriptions,
requireAuthentication,
signIn,
signOut,
confirmEmail,
- loadSubscriptions,
- getToken,
+ setIsSessionLoaded,
+ setToken,
+ setUser,
+ authorizer,
}
const value: SessionContextType = {
+ user: user(),
+ config: configuration(),
session,
subscriptions,
isSessionLoaded,
- author,
isAuthenticated,
+ author,
actions,
+ isAuthWithCallback,
}
- onMount(() => {
- loadSession()
- })
return {props.children}
}