o2o-site-AEO/solution/frontend/src/api/generated/auth/auth.ts
Mina Choi 48109fdc99 [feat] solution/backend,frontend: id/pw 가입 · 구글 로그인 — 계정을 만들 길이 없던 걸 연다
계정 생성 API 가 아예 없었다(그동안 users 를 손으로 INSERT 했다). 로그인 화면은 있는데
그 뒤에 설 계정을 만들 방법이 제품에 없는 상태였다.

- auth_service.signup: 가입 = **새 회사(테넌트) 1개 + 첫 계정 1개**. users.company_id 가
  NOT NULL 이고 모든 도메인이 company 로 스코프돼서, 회사 없는 계정은 아무것도 못 만든다
- services/external/google_identity: 구글 ID 토큰의 서명·iss·만료에 더해 **aud(우리 client_id)와
  email_verified 를 본다.** aud 검사가 빠지면 남의 앱에 발급된 '진짜' 구글 토큰으로 우리 계정에
  들어온다 — 서명도 발급자도 전부 맞으므로 다른 검사로는 안 걸린다
- users.provider/provider_uid 추가, password NULL 허용, id 20→64자(google_<sub> 가 20자를 넘는다).
  provider 에 server_default 를 같이 준 이유: ORM default 는 raw INSERT(테스트 시드)에 안 먹어서
  NOT NULL 컬럼이면 그 경로가 통째로 깨진다
- attempt_login: 소셜 계정을 먼저 끊는다. 안 끊으면 bcrypt 가 None 해시를 만나 500 이다
- 같은 이메일이라도 id/pw 계정과 구글 계정을 **잇지 않는다.** 이으면 계정 선점이다 —
  남의 이메일로 먼저 만들어 둔 계정에 그 사람의 구글 로그인이 들어간다 → DECISIONS 1-5
- LoginPage 는 admin 과 공유라 selfServe 로 갈랐다. admin 은 가입 링크도 구글 버튼도 안 뜬다
  (admin 라우터에 /signup 이 없어 404 가 난다)
- GOOGLE_CLIENT_ID 는 루트 .env 한 곳. compose 가 VITE_GOOGLE_CLIENT_ID 로 흘려보낸다 —
  두 곳에 적으면 백엔드 aud 대조와 화면 버튼이 조용히 갈라진다

★ 이미 도는 DB 는 init.sql 을 다시 적용해야 한다(말미 ALTER 섹션).

pytest: auth 13건 + 구글 토큰 검증 8건(진짜 RSA 서명으로 aud·iss·만료·email_verified·변조
거절 확인) 통과. 전체 527 passed / 8 failed(전부 기존 실패, 인증과 무관).
tsc·eslint·vite build 통과.
2026-09-02 09:33:59 +09:00

459 lines
15 KiB
TypeScript

/**
* Generated by orval v7.21.0 🍺
* Do not edit manually.
* Web4Ai API
* OpenAPI spec version: 0.1.0
*/
import {
useMutation,
useQuery
} from '@tanstack/react-query';
import type {
DataTag,
DefinedInitialDataOptions,
DefinedUseQueryResult,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UndefinedInitialDataOptions,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult
} from '@tanstack/react-query';
import type {
HTTPValidationError,
ReqGoogleLogin,
ReqLogin,
ReqSignup,
ReqUpdateMe,
ResLogin,
ResMe,
ResRefreshToken
} from '.././model';
import { customFetch } from '../../mutator/custom-fetch';
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
/**
* id/pw 로 로그인하고 JWT 토큰을 발급한다.
* @summary 로그인
*/
export const login = (
reqLogin: ReqLogin,
options?: SecondParameter<typeof customFetch>,signal?: AbortSignal
) => {
return customFetch<ResLogin>(
{url: `/v1/auth/login`, method: 'POST',
headers: {'Content-Type': 'application/json', },
data: reqLogin, signal
},
options);
}
export const getLoginMutationOptions = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof login>>, TError,{data: ReqLogin}, TContext>, request?: SecondParameter<typeof customFetch>}
): UseMutationOptions<Awaited<ReturnType<typeof login>>, TError,{data: ReqLogin}, TContext> => {
const mutationKey = ['login'];
const {mutation: mutationOptions, request: requestOptions} = options ?
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
options
: {...options, mutation: {...options.mutation, mutationKey}}
: {mutation: { mutationKey, }, request: undefined};
const mutationFn: MutationFunction<Awaited<ReturnType<typeof login>>, {data: ReqLogin}> = (props) => {
const {data} = props ?? {};
return login(data,requestOptions)
}
return { mutationFn, ...mutationOptions }}
export type LoginMutationResult = NonNullable<Awaited<ReturnType<typeof login>>>
export type LoginMutationBody = ReqLogin
export type LoginMutationError = void | HTTPValidationError
/**
* @summary 로그인
*/
export const useLogin = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof login>>, TError,{data: ReqLogin}, TContext>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient): UseMutationResult<
Awaited<ReturnType<typeof login>>,
TError,
{data: ReqLogin},
TContext
> => {
const mutationOptions = getLoginMutationOptions(options);
return useMutation(mutationOptions, queryClient);
}
/**
* id/pw 로 가입한다. 회사(테넌트) 1개가 함께 생기고, 성공하면 곧바로 로그인 토큰을 발급한다.
* @summary 회원가입
*/
export const signup = (
reqSignup: ReqSignup,
options?: SecondParameter<typeof customFetch>,signal?: AbortSignal
) => {
return customFetch<ResLogin>(
{url: `/v1/auth/signup`, method: 'POST',
headers: {'Content-Type': 'application/json', },
data: reqSignup, signal
},
options);
}
export const getSignupMutationOptions = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof signup>>, TError,{data: ReqSignup}, TContext>, request?: SecondParameter<typeof customFetch>}
): UseMutationOptions<Awaited<ReturnType<typeof signup>>, TError,{data: ReqSignup}, TContext> => {
const mutationKey = ['signup'];
const {mutation: mutationOptions, request: requestOptions} = options ?
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
options
: {...options, mutation: {...options.mutation, mutationKey}}
: {mutation: { mutationKey, }, request: undefined};
const mutationFn: MutationFunction<Awaited<ReturnType<typeof signup>>, {data: ReqSignup}> = (props) => {
const {data} = props ?? {};
return signup(data,requestOptions)
}
return { mutationFn, ...mutationOptions }}
export type SignupMutationResult = NonNullable<Awaited<ReturnType<typeof signup>>>
export type SignupMutationBody = ReqSignup
export type SignupMutationError = void | HTTPValidationError
/**
* @summary 회원가입
*/
export const useSignup = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof signup>>, TError,{data: ReqSignup}, TContext>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient): UseMutationResult<
Awaited<ReturnType<typeof signup>>,
TError,
{data: ReqSignup},
TContext
> => {
const mutationOptions = getSignupMutationOptions(options);
return useMutation(mutationOptions, queryClient);
}
/**
* 구글 ID 토큰(GIS credential)을 검증하고 JWT 토큰을 발급한다. 처음 온 계정은 그 자리에서 만든다.
* @summary 구글 로그인
*/
export const googleLogin = (
reqGoogleLogin: ReqGoogleLogin,
options?: SecondParameter<typeof customFetch>,signal?: AbortSignal
) => {
return customFetch<ResLogin>(
{url: `/v1/auth/google`, method: 'POST',
headers: {'Content-Type': 'application/json', },
data: reqGoogleLogin, signal
},
options);
}
export const getGoogleLoginMutationOptions = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof googleLogin>>, TError,{data: ReqGoogleLogin}, TContext>, request?: SecondParameter<typeof customFetch>}
): UseMutationOptions<Awaited<ReturnType<typeof googleLogin>>, TError,{data: ReqGoogleLogin}, TContext> => {
const mutationKey = ['googleLogin'];
const {mutation: mutationOptions, request: requestOptions} = options ?
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
options
: {...options, mutation: {...options.mutation, mutationKey}}
: {mutation: { mutationKey, }, request: undefined};
const mutationFn: MutationFunction<Awaited<ReturnType<typeof googleLogin>>, {data: ReqGoogleLogin}> = (props) => {
const {data} = props ?? {};
return googleLogin(data,requestOptions)
}
return { mutationFn, ...mutationOptions }}
export type GoogleLoginMutationResult = NonNullable<Awaited<ReturnType<typeof googleLogin>>>
export type GoogleLoginMutationBody = ReqGoogleLogin
export type GoogleLoginMutationError = void | HTTPValidationError
/**
* @summary 구글 로그인
*/
export const useGoogleLogin = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof googleLogin>>, TError,{data: ReqGoogleLogin}, TContext>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient): UseMutationResult<
Awaited<ReturnType<typeof googleLogin>>,
TError,
{data: ReqGoogleLogin},
TContext
> => {
const mutationOptions = getGoogleLoginMutationOptions(options);
return useMutation(mutationOptions, queryClient);
}
/**
* refresh 토큰으로 access 토큰을 재발급한다.
* @summary 액세스 토큰 갱신
*/
export const refreshToken = (
options?: SecondParameter<typeof customFetch>,signal?: AbortSignal
) => {
return customFetch<ResRefreshToken>(
{url: `/v1/auth/refresh_token`, method: 'POST', signal
},
options);
}
export const getRefreshTokenMutationOptions = <TError = void,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof refreshToken>>, TError,void, TContext>, request?: SecondParameter<typeof customFetch>}
): UseMutationOptions<Awaited<ReturnType<typeof refreshToken>>, TError,void, TContext> => {
const mutationKey = ['refreshToken'];
const {mutation: mutationOptions, request: requestOptions} = options ?
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
options
: {...options, mutation: {...options.mutation, mutationKey}}
: {mutation: { mutationKey, }, request: undefined};
const mutationFn: MutationFunction<Awaited<ReturnType<typeof refreshToken>>, void> = () => {
return refreshToken(requestOptions)
}
return { mutationFn, ...mutationOptions }}
export type RefreshTokenMutationResult = NonNullable<Awaited<ReturnType<typeof refreshToken>>>
export type RefreshTokenMutationError = void
/**
* @summary 액세스 토큰 갱신
*/
export const useRefreshToken = <TError = void,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof refreshToken>>, TError,void, TContext>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient): UseMutationResult<
Awaited<ReturnType<typeof refreshToken>>,
TError,
void,
TContext
> => {
const mutationOptions = getRefreshTokenMutationOptions(options);
return useMutation(mutationOptions, queryClient);
}
/**
* 유효한 access 토큰이 있어야 호출 가능. 토큰의 유저+회사 정보를 반환한다.
* @summary 내 정보
*/
export const me = (
options?: SecondParameter<typeof customFetch>,signal?: AbortSignal
) => {
return customFetch<ResMe>(
{url: `/v1/auth/me`, method: 'GET', signal
},
options);
}
export const getMeQueryKey = () => {
return [
`/v1/auth/me`
] as const;
}
export const getMeQueryOptions = <TData = Awaited<ReturnType<typeof me>>, TError = void>( options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof me>>, TError, TData>>, request?: SecondParameter<typeof customFetch>}
) => {
const {query: queryOptions, request: requestOptions} = options ?? {};
const queryKey = queryOptions?.queryKey ?? getMeQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof me>>> = ({ signal }) => me(requestOptions, signal);
return { queryKey, queryFn, ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof me>>, TError, TData> & { queryKey: DataTag<QueryKey, TData, TError> }
}
export type MeQueryResult = NonNullable<Awaited<ReturnType<typeof me>>>
export type MeQueryError = void
export function useMe<TData = Awaited<ReturnType<typeof me>>, TError = void>(
options: { query:Partial<UseQueryOptions<Awaited<ReturnType<typeof me>>, TError, TData>> & Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof me>>,
TError,
Awaited<ReturnType<typeof me>>
> , 'initialData'
>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient
): DefinedUseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> }
export function useMe<TData = Awaited<ReturnType<typeof me>>, TError = void>(
options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof me>>, TError, TData>> & Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof me>>,
TError,
Awaited<ReturnType<typeof me>>
> , 'initialData'
>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> }
export function useMe<TData = Awaited<ReturnType<typeof me>>, TError = void>(
options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof me>>, TError, TData>>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> }
/**
* @summary 내 정보
*/
export function useMe<TData = Awaited<ReturnType<typeof me>>, TError = void>(
options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof me>>, TError, TData>>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> } {
const queryOptions = getMeQueryOptions(options)
const query = useQuery(queryOptions, queryClient) as UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
query.queryKey = queryOptions.queryKey ;
return query;
}
/**
* 본인 이름/이메일/연락처/비밀번호를 수정한다(권한·소속·ID 변경 불가).
* @summary 내 정보 수정
*/
export const updateMe = (
reqUpdateMe: ReqUpdateMe,
options?: SecondParameter<typeof customFetch>,) => {
return customFetch<ResMe>(
{url: `/v1/auth/me`, method: 'PATCH',
headers: {'Content-Type': 'application/json', },
data: reqUpdateMe
},
options);
}
export const getUpdateMeMutationOptions = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateMe>>, TError,{data: ReqUpdateMe}, TContext>, request?: SecondParameter<typeof customFetch>}
): UseMutationOptions<Awaited<ReturnType<typeof updateMe>>, TError,{data: ReqUpdateMe}, TContext> => {
const mutationKey = ['updateMe'];
const {mutation: mutationOptions, request: requestOptions} = options ?
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
options
: {...options, mutation: {...options.mutation, mutationKey}}
: {mutation: { mutationKey, }, request: undefined};
const mutationFn: MutationFunction<Awaited<ReturnType<typeof updateMe>>, {data: ReqUpdateMe}> = (props) => {
const {data} = props ?? {};
return updateMe(data,requestOptions)
}
return { mutationFn, ...mutationOptions }}
export type UpdateMeMutationResult = NonNullable<Awaited<ReturnType<typeof updateMe>>>
export type UpdateMeMutationBody = ReqUpdateMe
export type UpdateMeMutationError = void | HTTPValidationError
/**
* @summary 내 정보 수정
*/
export const useUpdateMe = <TError = void | HTTPValidationError,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateMe>>, TError,{data: ReqUpdateMe}, TContext>, request?: SecondParameter<typeof customFetch>}
, queryClient?: QueryClient): UseMutationResult<
Awaited<ReturnType<typeof updateMe>>,
TError,
{data: ReqUpdateMe},
TContext
> => {
const mutationOptions = getUpdateMeMutationOptions(options);
return useMutation(mutationOptions, queryClient);
}