Skip to content

Commit

Permalink
refactor: better-auth
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Nov 26, 2024
1 parent c1e3c4d commit 7af2a78
Show file tree
Hide file tree
Showing 15 changed files with 659 additions and 467 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
},
"dependencies": {
"@antv/g2": "^4.2.11",
"@auth/core": "0.34.2",
"@bytebase/vue-kbar": "0.1.8",
"@codemirror/commands": "6.7.1",
"@codemirror/lang-markdown": "6.3.1",
Expand Down Expand Up @@ -57,6 +56,7 @@
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "5.5.0",
"ansi_up": "6.0.2",
"better-auth": "1.0.4",
"blurhash": "2.0.5",
"buffer": "6.0.3",
"canvas-confetti": "1.9.3",
Expand Down Expand Up @@ -131,4 +131,4 @@
"windicss": "3.5.6"
},
"packageManager": "[email protected]"
}
}
692 changes: 601 additions & 91 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion src/components/config-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { O } from 'crossbell.js/dist/index-LKLW_17H'
import { get, set } from 'lodash-es'
import { marked } from 'marked'
import {
Expand Down
4 changes: 2 additions & 2 deletions src/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { RouteName } from '~/router/name'
import { AppStore } from '~/stores/app'
import { UIStore } from '~/stores/ui'
import { removeToken, RESTManager } from '~/utils'
import { signOut } from '~/utils/authjs'
import { authClient } from '~/utils/authjs/auth'

import { configs } from '../../configs'
import { useStoreRef } from '../../hooks/use-store-ref'
Expand Down Expand Up @@ -308,7 +308,7 @@ const LogoutAvatarButton = defineComponent({
e.stopPropagation()
await RESTManager.api.user.logout.post({})
removeToken()
await signOut()
await authClient.signOut()
router.push({
name: RouteName.Login,
})
Expand Down
7 changes: 0 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@ import './index.css'

import { router } from './router'
import { attachTokenFromQuery, RESTManager } from './utils'
import { authConfigManager, getCsrfToken } from './utils/authjs'

authConfigManager.setConfig({
basePath: '/auth',
baseUrl: RESTManager.endpoint,
credentials: 'include',
})

attachTokenFromQuery()

Expand Down
212 changes: 24 additions & 188 deletions src/utils/authjs/auth.ts
Original file line number Diff line number Diff line change
@@ -1,188 +1,24 @@
import type {
BuiltInProviderType,
RedirectableProviderType,
} from '@auth/core/providers'
import type {
AuthClientConfig,
ClientSafeProvider,
LiteralUnion,
SignInAuthorizationParams,
SignInOptions,
SignInResponse,
SignOutParams,
SignOutResponse,
} from './client'

import { fetchData, parseUrl } from './client'

class AuthConfigManager {
private static instance: AuthConfigManager | null = null
_config: AuthClientConfig = {
baseUrl:
typeof window !== 'undefined'
? parseUrl(window.location.origin).origin
: '',
basePath:
typeof window !== 'undefined'
? parseUrl(window.location.origin).path
: '/api/auth',
credentials: 'same-origin',
_lastSync: 0,
_session: undefined,
_getSession: () => {},
}

static getInstance(): AuthConfigManager {
if (!AuthConfigManager.instance) {
AuthConfigManager.instance = new AuthConfigManager()
}
return AuthConfigManager.instance
}

setConfig(userConfig: Partial<AuthClientConfig>): void {
this._config = { ...this._config, ...userConfig }
}

getConfig(): AuthClientConfig {
return this._config
}
}

export const authConfigManager = AuthConfigManager.getInstance()
type ProvidersType = Record<
LiteralUnion<BuiltInProviderType>,
ClientSafeProvider
>

export async function getProviders() {
return fetchData<ProvidersType>('providers', authConfigManager.getConfig())
}

export async function getCsrfToken() {
const response = await fetchData<{ csrfToken: string }>(
'csrf',
authConfigManager.getConfig(),
)
return response?.csrfToken ?? ''
}

export async function signIn<
P extends RedirectableProviderType | undefined = undefined,
>(
provider?: LiteralUnion<
P extends RedirectableProviderType
? P | BuiltInProviderType
: BuiltInProviderType
>,
options?: SignInOptions,
authorizationParams?: SignInAuthorizationParams,
): Promise<
P extends RedirectableProviderType ? SignInResponse | undefined : undefined
> {
const { callbackUrl = window.location.href, redirect = true } = options ?? {}

const __AUTHJS: AuthClientConfig = authConfigManager.getConfig()

const href = `${__AUTHJS.baseUrl}${__AUTHJS.basePath}`

const providers = await getProviders()

if (!providers) {
window.location.href = `${href}/error`
return
}

if (!provider || !(provider in providers)) {
window.location.href = `${href}/signin?${new URLSearchParams({
callbackUrl,
})}`
return
}

const isCredentials = providers[provider].type === 'credentials'
const isEmail = providers[provider].type === 'email'
const isSupportingReturn = isCredentials || isEmail

const signInUrl = `${href}/${
isCredentials ? 'callback' : 'signin'
}/${provider}`

const csrfToken = await getCsrfToken()
const res = await fetch(
`${signInUrl}?${new URLSearchParams(authorizationParams)}`,
{
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Auth-Return-Redirect': '1',
},
// @ts-expect-error TODO: Fix this
body: new URLSearchParams({ ...options, csrfToken, callbackUrl }),
credentials: __AUTHJS.credentials,
},
)

const data = await res.json()

// TODO: Do not redirect for Credentials and Email providers by default in next major
if (redirect || !isSupportingReturn) {
const url = (data as any).url ?? callbackUrl
window.location.href = url
// If url contains a hash, the browser does not reload the page. We reload manually
if (url.includes('#')) {
window.location.reload()
}
return
}

const error = new URL((data as any).url).searchParams.get('error')

if (res.ok) {
await __AUTHJS._getSession({ event: 'storage' })
}

return {
error,
status: res.status,
ok: res.ok,
url: error ? null : (data as any).url,
} as any
}

/**
* Initiate a signout, by destroying the current session.
* Handles CSRF protection.
*/
export async function signOut<R extends boolean = true>(
options?: SignOutParams<R>,
): Promise<R extends true ? undefined : SignOutResponse> {
const { callbackUrl = window.location.href } = options ?? {}
const __AUTHJS: AuthClientConfig = authConfigManager.getConfig()
const href = `${__AUTHJS.baseUrl}${__AUTHJS.basePath}`
const csrfToken = await getCsrfToken()
const res = await fetch(`${href}/signout`, {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Auth-Return-Redirect': '1',
},
body: new URLSearchParams({ csrfToken, callbackUrl }),
credentials: __AUTHJS.credentials,
})
const data = await res.json()

if (options?.redirect ?? true) {
const url = (data as any).url ?? callbackUrl
window.location.href = url
// If url contains a hash, the browser does not reload the page. We reload manually
if (url.includes('#')) {
window.location.reload()
}
// @ts-expect-error TODO: Fix this
return
}

await __AUTHJS._getSession({ event: 'storage' })

return data as any
}
import { createAuthClient } from 'better-auth/client'

import { API_URL } from '~/constants/env'

export const authClient = createAuthClient({
baseURL: API_URL + '/auth',
fetchOptions: {
credentials: 'include',
},
})

export type AuthSocialProviders =
| 'apple'
| 'discord'
| 'facebook'
| 'github'
| 'google'
| 'microsoft'
| 'spotify'
| 'twitch'
| 'twitter'
| 'dropbox'
| 'linkedin'
| 'gitlab'
Loading

0 comments on commit 7af2a78

Please sign in to comment.