Client - authorize oauth client

This commit is contained in:
Sam 2022-06-07 17:45:08 +02:00
parent d8d88cda3a
commit 4be4f46c26
12 changed files with 196 additions and 17 deletions

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="new-oauth2-app"> <div id="new-oauth2-app">
<p id="new-oauth2-title">{{ $t('oauth2.ADD_A_NEW_APP') }}</p> <h1 id="new-oauth2-title">{{ $t('oauth2.ADD_A_NEW_APP') }}</h1>
<div id="apps-form"> <div id="apps-form">
<form @submit.prevent="createApp"> <form @submit.prevent="createApp">
<div class="form-items"> <div class="form-items">

View File

@ -0,0 +1,94 @@
<template>
<div id="authorize-oauth2-app" v-if="client.client_id">
<h1 id="authorize-oauth2-title">
<i18n-t keypath="oauth2.AUTHORIZE_APP">
<router-link :to="{ name: 'UserApp', params: { id: client.id } }">
{{ client.name }}
</router-link>
</i18n-t>
</h1>
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
<div class="oauth2-access">
<p>{{ $t('oauth2.APP_REQUESTING_ACCESS') }}</p>
<ul>
<li
class="client-scope"
v-for="scope in client.scope.split(' ')"
:key="scope"
>
{{ $t(`oauth2.APP.SCOPE.${scope.toUpperCase()}`) }}
</li>
</ul>
<div class="authorize-oauth2-buttons">
<button class="danger" @click="authorizeApp">
{{ $t('buttons.AUTHORIZE') }}
</button>
<button class="cancel" @click="$router.push('/profile/apps')">
{{ $t('buttons.CANCEL') }}
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ComputedRef, onBeforeMount } from 'vue'
import { useRoute } from 'vue-router'
import { OAUTH2_STORE, ROOT_STORE } from '@/store/constants'
import { IOAuth2Client } from '@/types/oauth'
import { useStore } from '@/use/useStore'
const route = useRoute()
const store = useStore()
const client: ComputedRef<IOAuth2Client> = computed(
() => store.getters[OAUTH2_STORE.GETTERS.CLIENT]
)
const errorMessages: ComputedRef<string | string[] | null> = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
)
onBeforeMount(() => loadApp())
function loadApp() {
if (route.query.client_id && typeof route.query.client_id === 'string') {
store.dispatch(
OAUTH2_STORE.ACTIONS.GET_CLIENT_BY_CLIENT_ID,
route.query.client_id
)
}
}
function authorizeApp() {
store.dispatch(OAUTH2_STORE.ACTIONS.AUTHORIZE_CLIENT, {
client_id: `${route.query.client_id}`,
redirect_uri: `${route.query.redirect_uri}`,
response_type: `${route.query.response_type}`,
scope: `${route.query.scope}`,
state: `${route.query.state}`,
})
}
</script>
<style scoped lang="scss">
@import '~@/scss/vars.scss';
#authorize-oauth2-app {
#authorize-oauth2-title {
font-size: 1.05em;
font-weight: bold;
padding: 0 $default-padding;
}
.oauth2-access {
padding: 0 $default-padding;
}
.authorize-oauth2-buttons {
display: flex;
button {
margin: $default-padding * 0.5;
}
}
}
</style>

View File

@ -44,7 +44,15 @@
{{ client.redirect_uris.length > 0 ? client.redirect_uris[0] : '' }} {{ client.redirect_uris.length > 0 ? client.redirect_uris[0] : '' }}
</dd> </dd>
<dt>{{ $t('oauth2.APP.SCOPE.LABEL') }}:</dt> <dt>{{ $t('oauth2.APP.SCOPE.LABEL') }}:</dt>
<dd>{{ client.scope }}</dd> <dd>
<span
class="client-scope"
v-for="scope in client.scope.split(' ')"
:key="scope"
>
{{ $t(`oauth2.APP.SCOPE.${scope.toUpperCase()}`) }}
</span>
</dd>
</dl> </dl>
<div class="app-buttons"> <div class="app-buttons">
<button class="danger" @click="updateDisplayModal(true)"> <button class="danger" @click="updateDisplayModal(true)">
@ -135,6 +143,9 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: $default-padding; gap: $default-padding;
} }
.client-scope {
padding-right: $default-padding * 0.5;
}
.no-description { .no-description {
font-style: italic; font-style: italic;
} }

View File

@ -1,5 +1,6 @@
{ {
"ACCOUNT-CONFIRMATION-RESEND": "Resend confirmation email", "ACCOUNT-CONFIRMATION-RESEND": "Resend confirmation email",
"AUTHORIZE": "Authorize",
"BACK": "Back", "BACK": "Back",
"CANCEL": "Cancel", "CANCEL": "Cancel",
"CLEAR_FILTER": "Clear filters", "CLEAR_FILTER": "Clear filters",

View File

@ -16,7 +16,9 @@
}, },
"APP_DELETION_CONFIRMATION": "Are you sure you want to delete this app?", "APP_DELETION_CONFIRMATION": "Are you sure you want to delete this app?",
"APP_CREATED_SUCCESSFULLY": "Application created successfully. Make sure to copy the secret now, it won't show up again.", "APP_CREATED_SUCCESSFULLY": "Application created successfully. Make sure to copy the secret now, it won't show up again.",
"APP_REQUESTING_ACCESS": "The application {0} is requesting:",
"APPS_LIST": "OAuth2 applications", "APPS_LIST": "OAuth2 applications",
"AUTHORIZE_APP": "Authorize {0} to use your account?",
"DELETE_APP": "Delete application", "DELETE_APP": "Delete application",
"NEW_APP": "New OAuth App", "NEW_APP": "New OAuth App",
"NO_DESCRIPTION": "no description", "NO_DESCRIPTION": "no description",

View File

@ -1,5 +1,6 @@
{ {
"ACCOUNT-CONFIRMATION-RESEND": "Envoyer à nouveau l'email de confirmation", "ACCOUNT-CONFIRMATION-RESEND": "Envoyer à nouveau l'email de confirmation",
"AUTHORIZE": "Autoriser",
"BACK": "Précédent", "BACK": "Précédent",
"CANCEL": "Annuler", "CANCEL": "Annuler",
"CLEAR_FILTER": "Réinitialiser", "CLEAR_FILTER": "Réinitialiser",

View File

@ -16,7 +16,9 @@
}, },
"APP_DELETION_CONFIRMATION": "Êtes-vous sûr de vouloir supprimer cette application ?", "APP_DELETION_CONFIRMATION": "Êtes-vous sûr de vouloir supprimer cette application ?",
"APP_CREATED_SUCCESSFULLY": "Application créée avec succès. Assurez-vous de copier le secret maintenant, il ne s'affichera plus.", "APP_CREATED_SUCCESSFULLY": "Application créée avec succès. Assurez-vous de copier le secret maintenant, il ne s'affichera plus.",
"APP_REQUESTING_ACCESS": "L'application {0} demande les accès suivants :",
"APPS_LIST": "Applications OAuth2", "APPS_LIST": "Applications OAuth2",
"AUTHORIZE_APP": "Autoriser {0} à utiliser votre compte ?",
"DELETE_APP": "Supprimer l'application", "DELETE_APP": "Supprimer l'application",
"NEW_APP": "Ajouter une App OAuth", "NEW_APP": "Ajouter une App OAuth",
"NO_DESCRIPTION": "pas de description", "NO_DESCRIPTION": "pas de description",

View File

@ -13,6 +13,7 @@ import UserInfosEdition from '@/components/User/ProfileEdition/UserInfosEdition.
import UserPictureEdition from '@/components/User/ProfileEdition/UserPictureEdition.vue' import UserPictureEdition from '@/components/User/ProfileEdition/UserPictureEdition.vue'
import UserPreferencesEdition from '@/components/User/ProfileEdition/UserPreferencesEdition.vue' import UserPreferencesEdition from '@/components/User/ProfileEdition/UserPreferencesEdition.vue'
import AddUserApp from '@/components/User/UserApps/AddUserApp.vue' import AddUserApp from '@/components/User/UserApps/AddUserApp.vue'
import AuthorizeUserApp from '@/components/User/UserApps/AuthorizeUserApp.vue'
import UserApps from '@/components/User/UserApps/index.vue' import UserApps from '@/components/User/UserApps/index.vue'
import UserApp from '@/components/User/UserApps/UserApp.vue' import UserApp from '@/components/User/UserApps/UserApp.vue'
import UserAppsList from '@/components/User/UserApps/UserAppsList.vue' import UserAppsList from '@/components/User/UserApps/UserAppsList.vue'
@ -174,6 +175,11 @@ const routes: Array<RouteRecordRaw> = [
name: 'AddUserApp', name: 'AddUserApp',
component: AddUserApp, component: AddUserApp,
}, },
{
path: 'authorize',
name: 'AuthorizeUserApp',
component: AuthorizeUserApp,
},
], ],
}, },
], ],

View File

@ -5,10 +5,60 @@ import router from '@/router'
import { OAUTH2_STORE, ROOT_STORE } from '@/store/constants' import { OAUTH2_STORE, ROOT_STORE } from '@/store/constants'
import { IOAuth2Actions, IOAuth2State } from '@/store/modules/oauth2/types' import { IOAuth2Actions, IOAuth2State } from '@/store/modules/oauth2/types'
import { IRootState } from '@/store/modules/root/types' import { IRootState } from '@/store/modules/root/types'
import { IOauth2ClientsPayload, IOAuth2ClientPayload } from '@/types/oauth' import {
IOauth2ClientsPayload,
IOAuth2ClientPayload,
IOAuth2ClientAuthorizePayload,
} from '@/types/oauth'
import { handleError } from '@/utils' import { handleError } from '@/utils'
const get_client = (
context: ActionContext<IOAuth2State, IRootState>,
url: string
) => {
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
authApi
.get(url)
.then((res) => {
if (res.data.status === 'success') {
context.commit(OAUTH2_STORE.MUTATIONS.SET_CLIENT, res.data.data.client)
} else {
handleError(context, null)
}
})
.catch((error) => handleError(context, error))
}
export const actions: ActionTree<IOAuth2State, IRootState> & IOAuth2Actions = { export const actions: ActionTree<IOAuth2State, IRootState> & IOAuth2Actions = {
[OAUTH2_STORE.ACTIONS.AUTHORIZE_CLIENT](
context: ActionContext<IOAuth2State, IRootState>,
payload: IOAuth2ClientAuthorizePayload
): void {
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
const form = new FormData()
form.set('client_id', payload.client_id)
form.set('response_type', payload.response_type)
form.set('scope', payload.scope)
form.set('confirm', 'true')
if (payload.state) {
form.set('state', payload.state)
}
authApi
.post('oauth/authorize', form, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then((res) => {
if (res.status == 200 && res.data.redirect_url) {
window.location.href = res.data.redirect_url
} else {
handleError(context, null)
}
})
.catch((error) => handleError(context, error))
},
[OAUTH2_STORE.ACTIONS.CREATE_CLIENT]( [OAUTH2_STORE.ACTIONS.CREATE_CLIENT](
context: ActionContext<IOAuth2State, IRootState>, context: ActionContext<IOAuth2State, IRootState>,
payload: IOAuth2ClientPayload payload: IOAuth2ClientPayload
@ -47,24 +97,17 @@ export const actions: ActionTree<IOAuth2State, IRootState> & IOAuth2Actions = {
}) })
.catch((error) => handleError(context, error)) .catch((error) => handleError(context, error))
}, },
[OAUTH2_STORE.ACTIONS.GET_CLIENT_BY_CLIENT_ID](
context: ActionContext<IOAuth2State, IRootState>,
client_id: string
): void {
get_client(context, `oauth/apps/${client_id}`)
},
[OAUTH2_STORE.ACTIONS.GET_CLIENT_BY_ID]( [OAUTH2_STORE.ACTIONS.GET_CLIENT_BY_ID](
context: ActionContext<IOAuth2State, IRootState>, context: ActionContext<IOAuth2State, IRootState>,
id: number id: number
): void { ): void {
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES) get_client(context, `oauth/apps/${id}/by_id`)
authApi
.get(`oauth/apps/${id}/by_id`)
.then((res) => {
if (res.data.status === 'success') {
context.commit(
OAUTH2_STORE.MUTATIONS.SET_CLIENT,
res.data.data.client
)
} else {
handleError(context, null)
}
})
.catch((error) => handleError(context, error))
}, },
[OAUTH2_STORE.ACTIONS.GET_CLIENTS]( [OAUTH2_STORE.ACTIONS.GET_CLIENTS](
context: ActionContext<IOAuth2State, IRootState>, context: ActionContext<IOAuth2State, IRootState>,

View File

@ -1,7 +1,9 @@
export enum OAuth2Actions { export enum OAuth2Actions {
AUTHORIZE_CLIENT = 'AUTHORIZE_CLIENT',
CREATE_CLIENT = 'CREATE_CLIENT', CREATE_CLIENT = 'CREATE_CLIENT',
DELETE_CLIENT = 'DELETE_CLIENT', DELETE_CLIENT = 'DELETE_CLIENT',
GET_CLIENTS = 'GET_CLIENTS', GET_CLIENTS = 'GET_CLIENTS',
GET_CLIENT_BY_CLIENT_ID = 'GET_CLIENT_BY_CLIENT_ID',
GET_CLIENT_BY_ID = 'GET_CLIENT_BY_ID', GET_CLIENT_BY_ID = 'GET_CLIENT_BY_ID',
} }

View File

@ -10,6 +10,7 @@ import { IRootState } from '@/store/modules/root/types'
import { IPagination } from '@/types/api' import { IPagination } from '@/types/api'
import { import {
IOAuth2Client, IOAuth2Client,
IOAuth2ClientAuthorizePayload,
IOAuth2ClientPayload, IOAuth2ClientPayload,
IOauth2ClientsPayload, IOauth2ClientsPayload,
} from '@/types/oauth' } from '@/types/oauth'
@ -21,6 +22,10 @@ export interface IOAuth2State {
} }
export interface IOAuth2Actions { export interface IOAuth2Actions {
[OAUTH2_STORE.ACTIONS.AUTHORIZE_CLIENT](
context: ActionContext<IOAuth2State, IRootState>,
payload: IOAuth2ClientAuthorizePayload
): void
[OAUTH2_STORE.ACTIONS.CREATE_CLIENT]( [OAUTH2_STORE.ACTIONS.CREATE_CLIENT](
context: ActionContext<IOAuth2State, IRootState>, context: ActionContext<IOAuth2State, IRootState>,
payload: IOAuth2ClientPayload payload: IOAuth2ClientPayload
@ -29,6 +34,10 @@ export interface IOAuth2Actions {
context: ActionContext<IOAuth2State, IRootState>, context: ActionContext<IOAuth2State, IRootState>,
id: number id: number
): void ): void
[OAUTH2_STORE.ACTIONS.GET_CLIENT_BY_CLIENT_ID](
context: ActionContext<IOAuth2State, IRootState>,
client_id: string
): void
[OAUTH2_STORE.ACTIONS.GET_CLIENT_BY_ID]( [OAUTH2_STORE.ACTIONS.GET_CLIENT_BY_ID](
context: ActionContext<IOAuth2State, IRootState>, context: ActionContext<IOAuth2State, IRootState>,
id: number id: number

View File

@ -21,3 +21,11 @@ export interface IOAuth2ClientPayload {
export interface IOauth2ClientsPayload { export interface IOauth2ClientsPayload {
page?: number page?: number
} }
export interface IOAuth2ClientAuthorizePayload {
client_id: string
redirect_uri: string
response_type: string
scope: string
state?: string
}