OAuth2 ๊ธฐ๋ฐ์ ์ธ์ฆ์ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋จผ์ @angular-flow/oauth2
๋ฅผ ์ค์นํฉ๋๋ค.
npm i @angular-flow/oauth2
app.config.ts
์ ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
// app.config.ts
...
export const appConfig: ApplicationConfig = {
providers: [
...
// oauth2 ๊ณต๊ธ์ ์ถ๊ฐ
provideOAuth2({
refreshBehavior: RefreshTokensUsecase // RefreshBehavior๋ฅผ ๊ตฌํํ๋ ํด๋์ค ๋ฑ๋ก
// tokenStorage: <TokenStorage ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ํด๋์ค> (์ ํ)
// ํ์ด๋ธ๋ฆฌ๋์น์ฑ์์๋ ์ ๊ณต๋๋ `CapacitorStorage`๋ก ๋ฑ๋กํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
}),
provideHttpClient(
withInterceptors([
// oauth2 ์ธํฐ์
ํฐ ์ถ๊ฐ
oauth2FlowInterceptor
])
),
]
};
๋๋ httpClient
๋ฅผ ํฌํจํ๋ ํ๋ก๋ฐ์ด๋๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค.
export const appConfig: ApplicationConfig = {
providers: [
...
provideOAuth2WithHttpClient({
refreshBehavior: RefreshTokensUsecase, // ํ์๋ฑ๋ก
interceptors: authMockInterceptors, // ์ ํ์ฌํญ
}),
],
};
OAuth2 ํ๋ก๋ฐ์ด๋๋ฅผ ๋ฑ๋กํ๊ธฐ ์ํด์ refreshBehavior
๋ฑ๋ก์ ํ์์
๋๋ค.
RefreshBehavior
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ํด๋์ค๋ฅผ ์์ฑํ๊ณ refresh
๋ฉ์๋์ ๋ฐํํ์
์ ๋ง์กฑํ๋๋ก ์ฝ๋๋ฅผ ์์ฑํด์ผํฉ๋๋ค.
@Injectable()
export class RefreshTokensUsecase implements RefreshBehavior {
private readonly httpClient = inject(HttpClient);
// @angular-flow/oauth2 ์์ ์ค์ ๋ ํ ํฐ ์คํ ๋ฆฌ์ง ์ฃผ์
private readonly tokenStorage = inject(TOKEN_STORAGE);
refresh(refreshToken: string): Observable<TokenResource> {
// ๋ฐฑ์๋์ ์ฝ์๋ ๋ฐฉ์์ผ๋ก http ํธ์ถ
const header = new HttpHeaders()
.set("Authorization", `Bearer ${refreshToken}`);
return this.httpClient.post<TokenResource>(REFRESH_TOKEN_MOCK_URL, {},{
headers: header,
// โ
context์ skipOAuth2Flow ์ปจํ
์คํธ๋ฅผ ์ฌ์ฉํด์ผํฉ๋๋ค.
// oauth2Flow ์ธํฐ์
ํฐ๋ฅผ ๋ฌด์ํ๊ณ ์งํํ๋๋ก ์ค์ ํฉ๋๋ค.
context: skipOAuth2Flow,
}).pipe(
// ์์ฒญ ์คํจ ์ ํ์ฒ๋ฆฌ
catchError((res: HttpErrorResponse) => {
window.alert(res.error.message);
return throwError(() => res);
})
);
}
}
ํ ํฐ ์คํ ๋ฆฌ์ง๋ฅผ ํตํด ํ ํฐ์ ์กฐํํ๊ฑฐ๋ ์ ์ฅ, ์ญ์ ํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ํตํด ํ ํฐ ๋ฆฌ์์ค๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ ๋ฌ ๋ฐ์ ๊ฒฝ์ฐ ์ ๊ณต๋๋ TOKEN_STORAGE
ํ๋ก๋ฐ์ด๋๋ฅผ ํ์ฉํ์ฌ ํ ํฐ ๋ฆฌ์์ค๋ฅผ ์ ์ฅํฉ๋๋ค.
Capacitor Preference
๊น์ง ๋์ํ๊ธฐ ๋๋ฌธ์ ์ ๊ณต๋๋ ๋ฉ์๋๋ค์ ๋ชจ๋ Promise
๋ฐํํ์
์ ๊ฐ์ง๋๋ค.@Injectable()
export class LoginUsecase {
private readonly httpClient = inject(HttpClient);
private readonly tokenStorage = inject(TOKEN_STORAGE);
async execute(dto: ReqLoginDTO) {
// http ์์ฒญ
const http$ = this.httpClient.post<TokenResource>(LOGIN_MOCK_URL, dto).pipe(
catchError((res: HttpErrorResponse) => {
window.alert(res.error.message);
return throwError(() => res);
})
);
const res = await lastValueFrom(http$);
// ํ ํฐ์คํ ๋ฆฌ์ง์ ์๋ต๊ฐ ๋ฐํ
await this.tokenStorage.set(res);
}
}
์ด์ ์ ์ญ์ ๋ฑ๋ก๋ httpClient
๋ฅผ ์ฌ์ฉํ์ฌ api ์์ฒญ์ ์งํํ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ ์ผ๋ค์ด ์ผ์ด๋ฉ๋๋ค.
๋ฌด์์ ํฌ์คํธ๊ฐ ์ถ์ฒ๋ฉ๋๋ค.
์ด๋ ํ ๊ธฐ๋ฅ ๋๋ ์ด๋ค ๋์์ฒด์ ๊ณ์ฐ๊ณผ ๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ์ํํ๋ ๋ฌธ์ ๋๋ ๊ธฐํธ๋ฅผ ์ฐ์ฐ์๋ผ ํ๋ค. Java์์์ ์ฐ์ฐ์๋ ํฌ๊ฒ ๋จํญ, ์ดํญ, ์ผํญ, ๋์ ์ฐ์ฐ์๋ก ๋๋๋ฉฐ, ์ดํญ ์ฐ์ฐ์๋ ์ฐ์ , ๋น๊ต, ๋ ผ๋ฆฌ ์ฐ์ฐ์๋ก ๋๋ ์ ์๋ค.
๋ณ์๋ฅผ ์ ์ธํ๋ ๊ฒ์ ํด๋น ์๋ฃํ์ ํฌ๊ธฐ๋งํผ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ฒ ๋ค๋ ๋ป ์ด๋ค. ๋ฉ๋ชจ๋ฆฌ์ ์์น๋ฅผ ๋ณ์๋ช ์ผ๋ก ์ฐธ์กฐํ๋ค.
์ฑ๊ธํค์ด๋ ์ด๋ค ํด๋์ค๊ฐ ์ต์ด ํ๋ฒ๋ง ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋นํ๊ณ ๊ทธ ๋ฉ๋ชจ๋ฆฌ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ๋์์ธ ํจํด์ ๋งํ๋ค.