mirror of
https://github.com/PretendoNetwork/website.git
synced 2026-08-23 17:17:02 -05:00
feat: add support for refresh tokens in internal stores
This commit is contained in:
27
server/api/auth/refresh.post.ts
Normal file
27
server/api/auth/refresh.post.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ClientError } from 'nice-grpc';
|
||||
import { RefreshSchema } from '#shared/api-types';
|
||||
import type { ApiAuthLogin } from '#shared/api-types';
|
||||
|
||||
export default defineEventHandler(async (event): Promise<ApiAuthLogin> => {
|
||||
const body = await readZodBody(event, RefreshSchema);
|
||||
const grpc = useApiGrpc(event);
|
||||
|
||||
try {
|
||||
const res = await grpc.login({
|
||||
refreshToken: body.token,
|
||||
grantType: 'refresh_token'
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken: res.accessToken,
|
||||
refreshToken: res.refreshToken
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof ClientError) {
|
||||
if (error.details === 'INVALID_ARGUMENT: Invalid or missing refresh token') {
|
||||
throw createApiError('UNAUTHENTICATED');
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
@@ -20,10 +20,7 @@ export function getAuthContext(event: H3Event): AuthContext | null {
|
||||
export function enforceLoggedIn(event: H3Event): AuthContext {
|
||||
const context = getAuthContext(event);
|
||||
if (!context) {
|
||||
throw createError({
|
||||
status: 401,
|
||||
message: 'This action requires authentication'
|
||||
});
|
||||
throw createApiError('UNAUTHENTICATED');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,11 @@ export const LoginSchema = z.object({
|
||||
});
|
||||
export type ApiAuthLoginRequest = z.infer<typeof LoginSchema>;
|
||||
|
||||
export const RefreshSchema = z.object({
|
||||
token: z.string()
|
||||
});
|
||||
export type ApiAuthRefreshRequest = z.infer<typeof RefreshSchema>;
|
||||
|
||||
export const RegisterSchema = z.object({
|
||||
email: z.email(),
|
||||
username: z.string(),
|
||||
|
||||
@@ -6,7 +6,8 @@ const apiErrorCodes = {
|
||||
STAFF_NO_DONATE: 'Staff members do not need to purchase tiers',
|
||||
INVALID_CAPTCHA: 'Invalid captcha, try again',
|
||||
INVALID_USERNAME: 'Could not find user',
|
||||
INVALID_PASSWORD: 'Incorrect password'
|
||||
INVALID_PASSWORD: 'Incorrect password',
|
||||
UNAUTHENTICATED: 'This request needs authentication'
|
||||
} as const;
|
||||
|
||||
export type ApiErrorCodes = keyof typeof apiErrorCodes;
|
||||
@@ -19,7 +20,8 @@ export const apiErrorCodeStatus: Record<ApiErrorCodes, number> = {
|
||||
STAFF_NO_DONATE: 400,
|
||||
INVALID_CAPTCHA: 400,
|
||||
INVALID_USERNAME: 400,
|
||||
INVALID_PASSWORD: 400
|
||||
INVALID_PASSWORD: 400,
|
||||
UNAUTHENTICATED: 401
|
||||
};
|
||||
|
||||
export function getTextForApiErrorCode(code: ApiErrorCodes): string {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export const apiFetch = $fetch.create({
|
||||
onRequest({ options }) {
|
||||
const token = useAuthStore().getToken();
|
||||
if (token) {
|
||||
options.headers.set('Authorization', 'Bearer ' + token);
|
||||
const tokens = useAuthStore().getTokens();
|
||||
if (tokens) {
|
||||
options.headers.set('Authorization', 'Bearer ' + tokens.accessToken);
|
||||
}
|
||||
},
|
||||
async onResponseError({ response }) {
|
||||
|
||||
@@ -8,8 +8,8 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
|
||||
const authStore = useAuthStore();
|
||||
authStore.refresh();
|
||||
const token = authStore.getToken();
|
||||
if (!token) {
|
||||
const tokens = authStore.getTokens();
|
||||
if (!tokens) {
|
||||
meStore.setMe(null);
|
||||
return; // No token
|
||||
}
|
||||
@@ -17,7 +17,7 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
try {
|
||||
const res = await $fetch<GetApiAuthMe>('/api/auth/me', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
Authorization: `Bearer ${tokens.accessToken}`
|
||||
}
|
||||
});
|
||||
meStore.setMe({
|
||||
|
||||
@@ -45,8 +45,15 @@ export function useAuthStore() {
|
||||
const refreshTokenCookie = useCookie<string | null>('refresh_token', oldOpts);
|
||||
const tokenTypeCookie = useCookie<string | null>('token_type', oldOpts);
|
||||
|
||||
function getToken() {
|
||||
return authState.value?.accessToken ?? null;
|
||||
function getTokens() {
|
||||
if (!authState.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
accessToken: authState.value.accessToken,
|
||||
refreshToken: authState.value.refreshToken
|
||||
};
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
@@ -76,7 +83,7 @@ export function useAuthStore() {
|
||||
}
|
||||
|
||||
return {
|
||||
getToken,
|
||||
getTokens,
|
||||
refresh,
|
||||
set,
|
||||
logout
|
||||
|
||||
Reference in New Issue
Block a user