879 lines
22 KiB
TypeScript
879 lines
22 KiB
TypeScript
/**
|
|
* Authentication API Service
|
|
*
|
|
* Handles all authentication-related API calls:
|
|
* - Pre-login (password verification + send verification code)
|
|
* - Resend verification code
|
|
* - Verify login (code verification + get tokens)
|
|
* - Google login
|
|
*/
|
|
import { ApiResponse, ApisauceInstance, create } from "apisauce"
|
|
|
|
import Config from "@/config"
|
|
|
|
import { getGeneralApiProblem, GeneralApiProblem } from "./apiProblem"
|
|
import type {
|
|
BindNewEmailRequest,
|
|
BindNewEmailResponse,
|
|
ChangePasswordRequest,
|
|
ChangePasswordResponse,
|
|
ForgotPasswordRequest,
|
|
ForgotPasswordResponse,
|
|
GetLoginHistoryParams,
|
|
GetLoginHistoryResponse,
|
|
GetProfileResponse,
|
|
GetSessionCountResponse,
|
|
GetSessionsParams,
|
|
GetSessionsResponse,
|
|
GoogleLoginRequest,
|
|
GoogleLoginResponse,
|
|
LogoutResponse,
|
|
PreLoginRequest,
|
|
PreLoginResponse,
|
|
PreRegisterRequest,
|
|
PreRegisterResponse,
|
|
RefreshTokenRequest,
|
|
RefreshTokenResponse,
|
|
ResendCodeRequest,
|
|
ResendCodeResponse,
|
|
ResendForgotPasswordRequest,
|
|
ResendForgotPasswordResponse,
|
|
ResendRegisterCodeRequest,
|
|
ResendRegisterCodeResponse,
|
|
ResetPasswordRequest,
|
|
ResetPasswordResponse,
|
|
RevokeOtherSessionsResponse,
|
|
RevokeSessionResponse,
|
|
SendEmailCodeRequest,
|
|
SendEmailCodeResponse,
|
|
UpdateProfileRequest,
|
|
UpdateProfileResponse,
|
|
VerifyCurrentEmailRequest,
|
|
VerifyCurrentEmailResponse,
|
|
VerifyLoginRequest,
|
|
VerifyLoginResponse,
|
|
VerifyRegisterRequest,
|
|
VerifyRegisterResponse,
|
|
} from "./authTypes"
|
|
|
|
const AUTH_API_CONFIG = {
|
|
url: Config.AUTH_API_URL || "https://auth.upay01.com",
|
|
timeout: 15000,
|
|
}
|
|
|
|
/**
|
|
* Auth API class for handling authentication requests
|
|
*/
|
|
export class AuthApi {
|
|
apisauce: ApisauceInstance
|
|
|
|
constructor() {
|
|
this.apisauce = create({
|
|
baseURL: AUTH_API_CONFIG.url,
|
|
timeout: AUTH_API_CONFIG.timeout,
|
|
headers: {
|
|
"Accept": "application/json",
|
|
"Content-Type": "application/json",
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Pre-login: Verify password and send verification code
|
|
*/
|
|
async preLogin(
|
|
email: string,
|
|
password: string,
|
|
language?: string,
|
|
): Promise<{ kind: "ok"; data: PreLoginResponse } | GeneralApiProblem> {
|
|
const payload: PreLoginRequest = { email, password }
|
|
if (language) payload.language = language
|
|
|
|
const response: ApiResponse<PreLoginResponse> = await this.apisauce.post(
|
|
"/api/auth/pre-login",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth preLogin error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resend verification code
|
|
*/
|
|
async resendCode(
|
|
email: string,
|
|
language?: string,
|
|
): Promise<{ kind: "ok"; data: ResendCodeResponse } | GeneralApiProblem> {
|
|
const payload: ResendCodeRequest = { email }
|
|
if (language) payload.language = language
|
|
|
|
const response: ApiResponse<ResendCodeResponse> = await this.apisauce.post(
|
|
"/api/auth/pre-login/resend",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth resendCode error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify login: Verify code and get tokens
|
|
*/
|
|
async verifyLogin(
|
|
email: string,
|
|
code: string,
|
|
): Promise<{ kind: "ok"; data: VerifyLoginResponse } | GeneralApiProblem> {
|
|
const payload: VerifyLoginRequest = { email, code }
|
|
|
|
const response: ApiResponse<VerifyLoginResponse> = await this.apisauce.post(
|
|
"/api/auth/login/verify",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth verifyLogin error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Google login
|
|
*/
|
|
async googleLogin(
|
|
idToken: string,
|
|
referralCode?: string,
|
|
): Promise<{ kind: "ok"; data: GoogleLoginResponse } | GeneralApiProblem> {
|
|
const payload: GoogleLoginRequest = { idToken }
|
|
if (referralCode) payload.referralCode = referralCode
|
|
|
|
const response: ApiResponse<GoogleLoginResponse> = await this.apisauce.post(
|
|
"/api/auth/google-login",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth googleLogin error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pre-register: Validate data and send verification code
|
|
*/
|
|
async preRegister(
|
|
username: string,
|
|
password: string,
|
|
email: string,
|
|
nickname?: string,
|
|
referralCode?: string,
|
|
language?: string,
|
|
): Promise<{ kind: "ok"; data: PreRegisterResponse } | GeneralApiProblem> {
|
|
const payload: PreRegisterRequest = { username, password, email }
|
|
if (nickname) payload.nickname = nickname
|
|
if (referralCode) payload.referralCode = referralCode
|
|
if (language) payload.language = language
|
|
|
|
const response: ApiResponse<PreRegisterResponse> = await this.apisauce.post(
|
|
"/api/auth/pre-register",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth preRegister error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resend register verification code
|
|
*/
|
|
async resendRegisterCode(
|
|
email: string,
|
|
language?: string,
|
|
): Promise<{ kind: "ok"; data: ResendRegisterCodeResponse } | GeneralApiProblem> {
|
|
const payload: ResendRegisterCodeRequest = { email }
|
|
if (language) payload.language = language
|
|
|
|
const response: ApiResponse<ResendRegisterCodeResponse> = await this.apisauce.post(
|
|
"/api/auth/pre-register/resend",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth resendRegisterCode error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify register: Verify code and create user
|
|
*/
|
|
async verifyRegister(
|
|
email: string,
|
|
code: string,
|
|
): Promise<{ kind: "ok"; data: VerifyRegisterResponse } | GeneralApiProblem> {
|
|
const payload: VerifyRegisterRequest = { email, code }
|
|
|
|
const response: ApiResponse<VerifyRegisterResponse> = await this.apisauce.post(
|
|
"/api/auth/register/verify",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth verifyRegister error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Forgot password: Send verification code
|
|
*/
|
|
async forgotPassword(
|
|
email: string,
|
|
language?: string,
|
|
): Promise<{ kind: "ok"; data: ForgotPasswordResponse } | GeneralApiProblem> {
|
|
const payload: ForgotPasswordRequest = { email }
|
|
if (language) payload.language = language
|
|
|
|
const response: ApiResponse<ForgotPasswordResponse> = await this.apisauce.post(
|
|
"/api/auth/forgot-password",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth forgotPassword error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resend forgot password verification code
|
|
*/
|
|
async resendForgotPasswordCode(
|
|
email: string,
|
|
language?: string,
|
|
): Promise<{ kind: "ok"; data: ResendForgotPasswordResponse } | GeneralApiProblem> {
|
|
const payload: ResendForgotPasswordRequest = { email }
|
|
if (language) payload.language = language
|
|
|
|
const response: ApiResponse<ResendForgotPasswordResponse> = await this.apisauce.post(
|
|
"/api/auth/forgot-password/resend",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth resendForgotPasswordCode error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset password: Verify code and set new password
|
|
*/
|
|
async resetPassword(
|
|
email: string,
|
|
code: string,
|
|
newPassword: string,
|
|
): Promise<{ kind: "ok"; data: ResetPasswordResponse } | GeneralApiProblem> {
|
|
const payload: ResetPasswordRequest = { email, code, newPassword }
|
|
|
|
const response: ApiResponse<ResetPasswordResponse> = await this.apisauce.post(
|
|
"/api/auth/reset-password",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth resetPassword error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logout: Revoke token
|
|
*/
|
|
async logoutApi(
|
|
accessToken: string,
|
|
): Promise<{ kind: "ok"; data: LogoutResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<LogoutResponse> = await this.apisauce.post(
|
|
"/api/auth/logout",
|
|
{},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth logout error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Profile APIs
|
|
// ============================================
|
|
|
|
/**
|
|
* Get user profile
|
|
*/
|
|
async getProfile(
|
|
accessToken: string,
|
|
): Promise<{ kind: "ok"; data: GetProfileResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<GetProfileResponse> = await this.apisauce.get(
|
|
"/api/auth/profile",
|
|
{},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth getProfile error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update user profile
|
|
*/
|
|
async updateProfile(
|
|
accessToken: string,
|
|
payload: UpdateProfileRequest,
|
|
): Promise<{ kind: "ok"; data: UpdateProfileResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<UpdateProfileResponse> = await this.apisauce.put(
|
|
"/api/auth/profile",
|
|
payload,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth updateProfile error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Password APIs
|
|
// ============================================
|
|
|
|
/**
|
|
* Change password
|
|
*/
|
|
async changePassword(
|
|
accessToken: string,
|
|
payload: ChangePasswordRequest,
|
|
): Promise<{ kind: "ok"; data: ChangePasswordResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<ChangePasswordResponse> = await this.apisauce.post(
|
|
"/api/auth/change-password",
|
|
payload,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth changePassword error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Token APIs
|
|
// ============================================
|
|
|
|
/**
|
|
* Refresh access token
|
|
*/
|
|
async refreshToken(
|
|
refreshToken: string,
|
|
): Promise<{ kind: "ok"; data: RefreshTokenResponse } | GeneralApiProblem> {
|
|
const payload: RefreshTokenRequest = { refreshToken }
|
|
|
|
const response: ApiResponse<RefreshTokenResponse> = await this.apisauce.post(
|
|
"/api/auth/refresh-token",
|
|
payload,
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth refreshToken error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Email APIs
|
|
// ============================================
|
|
|
|
/**
|
|
* Send email verification code (for changing email)
|
|
*/
|
|
async sendEmailCode(
|
|
accessToken: string,
|
|
email: string,
|
|
language?: string,
|
|
): Promise<{ kind: "ok"; data: SendEmailCodeResponse } | GeneralApiProblem> {
|
|
const payload: SendEmailCodeRequest = { email }
|
|
if (language) payload.language = language
|
|
|
|
const response: ApiResponse<SendEmailCodeResponse> = await this.apisauce.post(
|
|
"/api/email/send-verification-code",
|
|
payload,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth sendEmailCode error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify current email (Step 1 of email change)
|
|
*/
|
|
async verifyCurrentEmail(
|
|
accessToken: string,
|
|
currentCode: string,
|
|
): Promise<{ kind: "ok"; data: VerifyCurrentEmailResponse } | GeneralApiProblem> {
|
|
const payload: VerifyCurrentEmailRequest = {
|
|
action: "verify-current",
|
|
currentCode,
|
|
}
|
|
|
|
const response: ApiResponse<VerifyCurrentEmailResponse> = await this.apisauce.post(
|
|
"/api/email/change",
|
|
payload,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth verifyCurrentEmail error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bind new email (Step 2 of email change)
|
|
*/
|
|
async bindNewEmail(
|
|
accessToken: string,
|
|
newEmail: string,
|
|
newCode: string,
|
|
): Promise<{ kind: "ok"; data: BindNewEmailResponse } | GeneralApiProblem> {
|
|
const payload: BindNewEmailRequest = {
|
|
action: "bind-new",
|
|
newEmail,
|
|
newCode,
|
|
}
|
|
|
|
const response: ApiResponse<BindNewEmailResponse> = await this.apisauce.post(
|
|
"/api/email/change",
|
|
payload,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth bindNewEmail error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Session Management APIs
|
|
// ============================================
|
|
|
|
/**
|
|
* Get sessions/devices list
|
|
*/
|
|
async getSessions(
|
|
accessToken: string,
|
|
params?: GetSessionsParams,
|
|
): Promise<{ kind: "ok"; data: GetSessionsResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<GetSessionsResponse> = await this.apisauce.get(
|
|
"/api/sessions",
|
|
params,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth getSessions error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get active session count
|
|
*/
|
|
async getSessionCount(
|
|
accessToken: string,
|
|
): Promise<{ kind: "ok"; data: GetSessionCountResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<GetSessionCountResponse> = await this.apisauce.get(
|
|
"/api/sessions/count",
|
|
{},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth getSessionCount error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get login history
|
|
*/
|
|
async getLoginHistory(
|
|
accessToken: string,
|
|
params?: GetLoginHistoryParams,
|
|
): Promise<{ kind: "ok"; data: GetLoginHistoryResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<GetLoginHistoryResponse> = await this.apisauce.get(
|
|
"/api/sessions/history",
|
|
params,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth getLoginHistory error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Revoke a specific session (remote logout device)
|
|
*/
|
|
async revokeSession(
|
|
accessToken: string,
|
|
sessionId: string,
|
|
): Promise<{ kind: "ok"; data: RevokeSessionResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<RevokeSessionResponse> = await this.apisauce.delete(
|
|
`/api/sessions/${sessionId}`,
|
|
{},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth revokeSession error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Revoke all other sessions (logout all other devices)
|
|
*/
|
|
async revokeOtherSessions(
|
|
accessToken: string,
|
|
): Promise<{ kind: "ok"; data: RevokeOtherSessionsResponse } | GeneralApiProblem> {
|
|
const response: ApiResponse<RevokeOtherSessionsResponse> = await this.apisauce.post(
|
|
"/api/sessions/revoke-others",
|
|
{},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!response.ok) {
|
|
const problem = getGeneralApiProblem(response)
|
|
if (problem) return problem
|
|
}
|
|
|
|
try {
|
|
const data = response.data
|
|
if (!data) return { kind: "bad-data" }
|
|
return { kind: "ok", data }
|
|
} catch (e) {
|
|
if (__DEV__ && e instanceof Error) {
|
|
console.error(`Auth revokeOtherSessions error: ${e.message}`)
|
|
}
|
|
return { kind: "bad-data" }
|
|
}
|
|
}
|
|
}
|
|
|
|
// Singleton instance
|
|
export const authApi = new AuthApi()
|