Skip to content

Commit 2684db6

Browse files
committed
fix : 403 check Unauthorized
1 parent eced64d commit 2684db6

4 files changed

Lines changed: 85 additions & 51 deletions

File tree

‎.prettierrc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"singleQuote": false,
44
"jsxSingleQuote": false,
55
"trailingComma": "all",
6-
"printWidth": 90,
6+
"printWidth": 120,
77
"tabWidth": 2,
88
"arrowParens": "always",
99
"bracketSpacing": true,

‎contexts/s3-context.tsx‎

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ interface S3ContextValue {
1717
isReady: boolean
1818
}
1919

20-
const S3Context = createContext<S3ContextValue>({ client: null, isReady: false })
20+
const S3Context = createContext<S3ContextValue>({
21+
client: null,
22+
isReady: false,
23+
})
2124

2225
export function S3Provider({ children }: { children: React.ReactNode }) {
2326
const { credentials, isAuthenticated, logout } = useAuth()
@@ -54,42 +57,54 @@ export function S3Provider({ children }: { children: React.ReactNode }) {
5457

5558
/* eslint-disable @typescript-eslint/no-explicit-any -- AWS SDK middleware types are complex */
5659
client.middlewareStack.add(
57-
((next: any) =>
58-
async (args: any) => {
59-
try {
60-
const response = (await next(args)) as S3Response
61-
62-
if (response.response?.body && typeof response.response.body === "string") {
63-
const body = response.response.body.trim()
64-
if (body.match(/^<\?xml[^>]*\?><[^>]*><\/[^>]*>$/)) {
65-
const tagName = body.match(/<([^>]*)><\/\1>/)?.[1]
66-
if (tagName) {
67-
const propertyName = tagName.replace(/(?:^|_)([a-z])/g, (_, letter: string) =>
68-
letter.toUpperCase()
69-
)
70-
return {
71-
response: response.response,
72-
[propertyName]: null,
73-
}
60+
((next: any) => async (args: any) => {
61+
try {
62+
const response = (await next(args)) as S3Response
63+
64+
if (response.response?.body && typeof response.response.body === "string") {
65+
const body = response.response.body.trim()
66+
if (body.match(/^<\?xml[^>]*\?><[^>]*><\/[^>]*>$/)) {
67+
const tagName = body.match(/<([^>]*)><\/\1>/)?.[1]
68+
if (tagName) {
69+
const propertyName = tagName.replace(/(?:^|_)([a-z])/g, (_, letter: string) => letter.toUpperCase())
70+
return {
71+
response: response.response,
72+
[propertyName]: null,
7473
}
7574
}
7675
}
76+
}
7777

78-
return response
79-
} catch (error: unknown) {
80-
const err = error as { $metadata?: { httpStatusCode?: number }; Code?: string }
81-
if (err?.$metadata?.httpStatusCode === 401) {
78+
return response
79+
} catch (error: unknown) {
80+
const err = error as {
81+
$metadata?: { httpStatusCode?: number }
82+
Code?: string
83+
name?: string
84+
message?: string
85+
}
86+
if (err?.$metadata?.httpStatusCode === 401) {
87+
logout()
88+
window.location.href = getLoginRoute()
89+
return { response: { statusCode: 401, headers: {} } }
90+
}
91+
if (err?.$metadata?.httpStatusCode === 403) {
92+
const codeText = (err?.Code || err?.name || "").toLowerCase()
93+
const isUnauthorizedAccess = codeText === "unauthorizedaccess"
94+
const isInvalidAccessKey = codeText === "invalidaccesskeyid"
95+
if (isUnauthorizedAccess || isInvalidAccessKey) {
8296
logout()
8397
window.location.href = getLoginRoute()
8498
return { response: { statusCode: 401, headers: {} } }
8599
}
86-
if (err?.Code) {
87-
throw new Error(err.Code)
88-
}
89-
throw error
90100
}
91-
}) as any,
92-
{ step: "deserialize", name: "handleXmlResponse" }
101+
if (err?.Code) {
102+
throw new Error(err.Code)
103+
}
104+
throw error
105+
}
106+
}) as any,
107+
{ step: "deserialize", name: "handleXmlResponse" },
93108
)
94109
/* eslint-enable @typescript-eslint/no-explicit-any */
95110

‎lib/api-client.ts‎

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ export class ApiClient {
3232
this.errorHandler = handler
3333
}
3434

35-
async request(
36-
url: string,
37-
options: RequestOptions = {},
38-
parseJson: boolean = true
39-
) {
35+
async request(url: string, options: RequestOptions = {}, parseJson: boolean = true) {
4036
url = this.config?.baseUrl ? joinURL(this.config?.baseUrl, url) : url
4137
options.headers = { ...this.config?.headers, ...options.headers }
4238
if (
@@ -59,7 +55,10 @@ export class ApiClient {
5955
logger.log("[request] url:", url)
6056
logger.log("[request] options:", options)
6157

62-
const response = await this.$api.fetch(url, options as RequestInit & { body?: BodyInit | null; aws?: Record<string, unknown> })
58+
const response = await this.$api.fetch(
59+
url,
60+
options as RequestInit & { body?: BodyInit | null; aws?: Record<string, unknown> },
61+
)
6362

6463
logger.log("[request] response:", response)
6564

@@ -70,8 +69,39 @@ export class ApiClient {
7069
return
7170
}
7271
if (response.status === 403) {
73-
if (this.errorHandler) {
74-
await this.errorHandler.handle403()
72+
try {
73+
const cloned = response.clone()
74+
let codeText = ""
75+
try {
76+
const data = (await cloned.json()) as Record<string, unknown>
77+
let code: string | undefined = (data?.code as string | undefined) || (data?.Code as string | undefined)
78+
if (data && typeof data === "object" && "error" in data) {
79+
const errObj = (data as { error?: unknown }).error
80+
if (errObj && typeof errObj === "object") {
81+
const e = errObj as { code?: unknown }
82+
if (typeof e.code === "string") code = code ?? e.code
83+
}
84+
}
85+
codeText = typeof code === "string" ? code : ""
86+
} catch {
87+
codeText = ""
88+
}
89+
90+
const normalizedCode = codeText.toLowerCase()
91+
const isUnauthorizedAccess = normalizedCode === "unauthorizedaccess"
92+
const isInvalidAccessKey = normalizedCode === "invalidaccesskeyid"
93+
94+
if (this.errorHandler) {
95+
if (isUnauthorizedAccess || isInvalidAccessKey) {
96+
await this.errorHandler.handle401()
97+
} else {
98+
await this.errorHandler.handle403()
99+
}
100+
}
101+
} catch {
102+
if (this.errorHandler) {
103+
await this.errorHandler.handle403()
104+
}
75105
}
76106
return
77107
}
@@ -81,11 +111,7 @@ export class ApiClient {
81111
throw new Error(errorMsg)
82112
}
83113

84-
if (
85-
response.status === 204 ||
86-
response.headers.get("content-length") === "0" ||
87-
!response.body
88-
) {
114+
if (response.status === 204 || response.headers.get("content-length") === "0" || !response.body) {
89115
return null
90116
}
91117

@@ -101,11 +127,7 @@ export class ApiClient {
101127
}
102128

103129
async *streamRequest(url: string, options: RequestOptions = {}) {
104-
const response = (await this.request(
105-
url,
106-
{ method: "GET", ...options },
107-
false
108-
)) as Response | null
130+
const response = (await this.request(url, { method: "GET", ...options }, false)) as Response | null
109131

110132
if (!response?.body) {
111133
throw new Error("No response body")

‎lib/api-error-handler.ts‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import type {
2-
ApiErrorHandlerCallback,
3-
IApiErrorHandler,
4-
} from "@/types/api"
1+
import type { ApiErrorHandlerCallback, IApiErrorHandler } from "@/types/api"
52

63
export class ApiErrorHandler implements IApiErrorHandler {
74
private onUnauthorized?: ApiErrorHandlerCallback

0 commit comments

Comments
 (0)