0.9.6 β€’ Published 11 months ago

@angular-flow/oauth2 v0.9.6

Weekly downloads
-
License
-
Repository
-
Last release
11 months ago

Angular OAuth2 Flow 🫧

OAuth2 기반의 인증을 λ„μ™€μ£ΌλŠ” 라이브러리

인증 흐름

image

μ„€μ •

λ¨Όμ € @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 λ©”μ„œλ“œμ˜ λ°˜ν™˜νƒ€μž…μ„ λ§Œμ‘±ν•˜λ„λ‘ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ•Όν•©λ‹ˆλ‹€.

  • refresh λ©”μ„œλ“œλŠ” OAuth2 λ‚΄λΆ€ μ‹œμŠ€ν…œμ—μ„œ 호좜 λ©λ‹ˆλ‹€. 이 λ•Œ μΈμžκ°’μœΌλ‘œ ν˜„μž¬ μ‹œμ μ˜ refreshToken을 λ„˜κΈ°κΈ° λ•Œλ¬Έμ— ν•΄λ‹Ή 값을 ν™œμš©ν•˜μ—¬ μš”μ²­μ„ 보낼 수 μžˆμŠ΅λ‹ˆλ‹€.
@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 μš”μ²­μ„ μ§„ν–‰ν•  경우 λ‹€μŒκ³Ό 같은 일듀이 μΌμ–΄λ‚©λ‹ˆλ‹€.

  • (ν† ν°μž¬λ°œκΈ‰μ„ μ œμ™Έν•œ) λͺ¨λ“  API μš”μ²­μ— accessToken μœ λ¬΄μ— 따라 Authorization 해더에 Bearer ν˜•νƒœλ‘œ μ—‘μ„ΈμŠ€ν† ν°μ„ μ£Όμž…ν•©λ‹ˆλ‹€.
  • oauth2 인터셉터λ₯Ό κ±°μΉ˜λŠ” μš”μ²­λ“€μ€ μ‹€νŒ¨ 응닡(401 Unauthorization error) μ‹œ λ“±λ‘λœ ν† ν°μž¬λ°œκΈ‰ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 토큰 μž¬λ°œκΈ‰ μš”μ²­μ„ μ§„ν–‰ν•©λ‹ˆλ‹€.
    • μ‹€νŒ¨ν–ˆλ˜ μš”μ²­λ“€μ€ λŒ€κΈ°μ—΄ 큐에 μ €μž₯λ©λ‹ˆλ‹€.
  • 토큰 μž¬λ°œκΈ‰ μš”μ²­μ΄ μ‹€νŒ¨λ‘œ 끝날 경우, 토큰 μž¬λ°œκΈ‰ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ ν΄λž˜μŠ€μ—μ„œ ν›„μ²˜λ¦¬λ₯Ό ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • μ‹€νŒ¨ μ‹œ oauth2 μ„œλΉ„μŠ€μ—μ„œ ν† ν°μŠ€ν† λ¦¬μ§€λ₯Ό μžλ™μœΌλ‘œ λΉ„μ›λ‹ˆλ‹€.
  • 토큰 μž¬λ°œκΈ‰ μš”μ²­μ΄ 성곡적이면 토큰 λ¦¬μ†ŒμŠ€λ₯Ό μžλ™μœΌλ‘œ 토큰 μŠ€ν† λ¦¬μ§€μ— λ‹€μ‹œ μ €μž₯ν•˜κ²Œ λ©λ‹ˆλ‹€.
    • λŒ€κΈ°μ—΄ 큐에 μ €μž₯된 μš”μ²­λ“€λ„ μΌκ΄„μ μœΌλ‘œ λ‹€μ‹œ μš”μ²­ν•˜κ²Œ λ©λ‹ˆλ‹€.
0.9.6

11 months ago

0.9.5

11 months ago

0.9.4

11 months ago

0.9.3

11 months ago

0.9.2

11 months ago

0.9.1

11 months ago

0.0.1

11 months ago