feat: add frontend OAuth authorize route and component
Add /oauth/authorize frontend route with OAuthAuthorize.vue that handles the OAuth authorization flow: validates required query params, calls the API to generate an authorization code, and redirects to the callback URI. Authentication is handled by the standard router guard.
This commit is contained in:
parent
e5987acf80
commit
0471f8a729
|
|
@ -54,6 +54,7 @@
|
|||
"authenticating": "Authenticating…",
|
||||
"openIdStateError": "State does not match, refusing to continue!",
|
||||
"openIdGeneralError": "An error occurred while authenticating against the third party.",
|
||||
"oauthMissingParams": "Missing required OAuth parameters: {params}",
|
||||
"logout": "Logout",
|
||||
"emailInvalid": "Please enter a valid email address.",
|
||||
"usernameRequired": "Please provide a username.",
|
||||
|
|
|
|||
|
|
@ -394,6 +394,11 @@ const router = createRouter({
|
|||
name: 'openid.auth',
|
||||
component: OpenIdAuth,
|
||||
},
|
||||
{
|
||||
path: '/oauth/authorize',
|
||||
name: 'oauth.authorize',
|
||||
component: () => import('@/views/user/OAuthAuthorize.vue'),
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@ async function submit() {
|
|||
try {
|
||||
await authStore.login(credentials)
|
||||
authStore.setNeedsTotpPasscode(false)
|
||||
|
||||
redirectIfSaved()
|
||||
} catch (e) {
|
||||
if (e.response?.data.code === 1017 && !credentials.totpPasscode) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div>
|
||||
<Message
|
||||
v-if="errorMessage"
|
||||
variant="danger"
|
||||
>
|
||||
{{ errorMessage }}
|
||||
</Message>
|
||||
<Message v-if="loading">
|
||||
{{ $t('user.auth.authenticating') }}
|
||||
</Message>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, onMounted} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import {getErrorText} from '@/message'
|
||||
import Message from '@/components/misc/Message.vue'
|
||||
import {AuthenticatedHTTPFactory} from '@/helpers/fetcher'
|
||||
|
||||
defineOptions({name: 'OAuthAuthorize'})
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const route = useRoute()
|
||||
|
||||
const loading = ref(true)
|
||||
const errorMessage = ref('')
|
||||
|
||||
const requiredParams = [
|
||||
'response_type',
|
||||
'client_id',
|
||||
'redirect_uri',
|
||||
'code_challenge',
|
||||
'code_challenge_method',
|
||||
] as const
|
||||
|
||||
async function authorize() {
|
||||
// Validate required query parameters
|
||||
const missing = requiredParams.filter(p => !route.query[p])
|
||||
if (missing.length > 0) {
|
||||
errorMessage.value = t('user.auth.oauthMissingParams', {params: missing.join(', ')})
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const HTTP = AuthenticatedHTTPFactory()
|
||||
const response = await HTTP.post('oauth/authorize', {
|
||||
response_type: route.query.response_type,
|
||||
client_id: route.query.client_id,
|
||||
redirect_uri: route.query.redirect_uri,
|
||||
state: route.query.state,
|
||||
code_challenge: route.query.code_challenge,
|
||||
code_challenge_method: route.query.code_challenge_method,
|
||||
})
|
||||
|
||||
const {code, redirect_uri, state} = response.data
|
||||
|
||||
const redirectUrl = new URL(redirect_uri)
|
||||
redirectUrl.searchParams.set('code', code)
|
||||
if (state) {
|
||||
redirectUrl.searchParams.set('state', state)
|
||||
}
|
||||
|
||||
window.location.href = redirectUrl.toString()
|
||||
} catch (e) {
|
||||
errorMessage.value = getErrorText(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => authorize())
|
||||
</script>
|
||||
|
|
@ -80,8 +80,9 @@ async function authenticateWithCode() {
|
|||
provider: route.params.provider,
|
||||
code: route.query.code,
|
||||
})
|
||||
|
||||
redirectIfSaved()
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
errorMessage.value = getErrorText(e)
|
||||
} finally {
|
||||
localStorage.removeItem('authenticating')
|
||||
|
|
|
|||
Loading…
Reference in New Issue