Java/인증 및 인가, IAM

[Java/IAM] Spring Boot 환경에서 Keycloak 활용하기 -4 : Identity providers Social 소셜 로그인 구현(Google)

adjh54 2025. 2. 7. 20:01
728x170
해당 글에서는 Keycloak을 활용하는 방법으로 Identity providers Social 소셜 로그인 구현(Google)을 하는 방법에 대해 알아봅니다.


 

 

 

💡 [참고] Keycloak 초기 구성에서부터 활용방법에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
분류 주제 URL
Docker Docker Compose를 이용한 Keycloak 환경 구성 및 실행 방법 https://adjh54.tistory.com/644
환경설정 Google Cloud Console OAuth 2.0 API 액세스 환경 설정하기 https://adjh54.tistory.com/657
     
이해하기 Keycloak 이해하기 -1 : 구성 요소, 인증 처리과정, 주요 기능 https://adjh54.tistory.com/645
이해하기 Keycloak 이해하기 -2 : SAML/OIDC 프로토콜, 인증 흐름(Authentication flow) 종류 https://adjh54.tistory.com/646
이해하기 Keycloak 이해하기 -3 : 기본 환경 구성 및 로그인/로그아웃 구현 https://adjh54.tistory.com/647
이해하기 Keycloak 이해하기 -4 : Keycloak 권한 및 종류 https://adjh54.tistory.com/655
     
구성하기 Spring Boot 환경에서 Keycloak 활용하기 -1 : OIDC 인증 흐름 구현(Standard Flow) https://adjh54.tistory.com/648
구성하기 Spring Boot 환경에서 Keycloak 활용하기 -2 : OIDC 인증 흐름 구현(Direct Access Grants, Implicit Flow) https://adjh54.tistory.com/649
구성하기 Spring Boot 환경에서 Keycloak 활용하기 -3 : OIDC 인증 흐름 구현(Service Accounts Roles) https://adjh54.tistory.com/654
구성하기 Spring Boot 환경에서 Keycloak 활용하기 -4 : Identity providers Social 소셜 로그인 구현(Google) https://adjh54.tistory.com/658
     
Github Spring Boot Keycloak 관련 Repository https://github.com/adjh54ir/blog-codes/tree/main/spring-boot-keycloakhttps://github.com/adjh54ir/blog-codes/tree/main/spring-boot-keycloak-sub

 

 

1) Keycloak


💡 Keycloak

- Red Hat에서 개발한 오픈소스 Identity and Access Management(IAM) 솔루션입니다. 현대적인 애플리케이션과 서비스를 위한 인증 및 권한 부여 기능을 제공하는 인증 서버(Authentication Server)의 기능을 수행합니다.


- Keycloack에서는 여러 플랫폼에서 중앙 집중식 인증 서버로 동작을 합니다. 주요한 기능은 서로 다른 도메인에서 실행되는 애플리케이션 간의 SSO를 지원하거나 REST API 기반에 접근제어 토큰에 대한 인증 제공 및 세션 타임아웃, 동시 로그인 제한과 같은 다양한 세션 기능을 담당합니다.

 

Keycloak

Single-Sign On Users authenticate with Keycloak rather than individual applications. This means that your applications don't have to deal with login forms, authenticating users, and storing users. Once logged-in to Keycloak, users don't have to login again

www.keycloak.org

 

1. Keycloak 사용목적


기능 설명
중앙 집중식 인증 관리 여러 애플리케이션에 대한 인증을 한 곳에서 관리할 수 있어 보안 정책 적용과 유지보수가 용이합니다.
SSO(Single Sign-On) 지원 사용자가 한 번의 로그인으로 여러 애플리케이션에 접근할 수 있어 사용자 경험이 향상됩니다.
다양한 인증 프로토콜 OpenID Connect, SAML 2.0 등 표준 프로토콜을 지원하여 다양한 시스템과의 통합이 가능합니다.
소셜 로그인 통합 Google, Facebook 등 소셜 로그인을 쉽게 구현할 수 있습니다.
강력한 보안 기능 2단계 인증, 비밀번호 정책, 세션 관리 등 다양한 보안 기능을 제공합니다.
오픈소스 무료로 사용 가능하며, 활발한 커뮤니티 지원과 지속적인 업데이트가 이루어집니다.
확장성 REST API를 통한 통합이 용이하며, 커스터마이징이 가능한 유연한 아키텍처를 제공합니다.

 

 

2) Google 로그인 연동하기


 

💡 Google 로그인 연동하기

- Keycloak에서 Google 로그인과 연동하여서 SSO를 구현합니다.

 

1. Keycloak - Google Social Login - Spring Boot Appplication 처리 과정


💡 Keycloak - Google Social Login - Spring Boot Appplication 처리 과정

1. Keycloak URL 접근 및 Google 로그인 수행: Client → Keycloak 관리 서버
- 최초 클라이언트는 Keycloak 로그인 페이지로 접근을 합니다. 해당 접근 시, 최종 리턴이 되는 Spring Boot Application의 엔드포인트 redirect_uri를 함께 파라미터에 담아서 전송합니다.

- 접속 URL 형태 : /reams/{realm-name}/protocol/openid-connect/auth?client_id={keyclaokClient}?response_type=code&redirect_uri={applicationRedirectUri}
- 사용한 URL : http://localhost:9001/realms/dev-realm/protocol/openid-connect/auth?client_id=spring-boot-app&response_type=code&redirect_uri=http://localhost:8080/api/v1/keycloak/callback


2. Google 로그인이 포함된 Keycloak 로그인 화면을 클라이언트에게 제공 : Keycloak 관리 서버 → Keycloak 로그인 화면
- Keycloak 관리서버에서는 Keylcloak 로그인화면을 제공합니다.


3. Google 로그인 아이콘을 누른 경우, 로그인 페이지를 제공 : Keycloak 로그인 화면 → Google
- Keycloak 로그인 화면에서 Google 로그인 아이콘을 누르면 Google Login 페이지를 제공합니다.


4. Google 로그인 성공 후 Keycloak 리다이렉트 URL로 반환해 줍니다. : Google → Keycloak 관리 서버
- Google에서 지정한 redirect_uri를 기반으로 Keycloak 관리서버로 리다이렉트 됩니다.
- 구성한 URL: http://localhost:9001/realms/dev-realm/broker/google/endpoint


5. Keycloak에서는 최초 클라이언트가 파라미터로 전달한 redirect_uri 엔드포인트로 리턴하며 반환 값으로 인가 코드(Auth Code)를 전달 : Keycloak 관리 서버 → KeycloakController
- Keycloak에서는 최초 클라이언트가 파라미터로 함께 보냈던 redirect_uri 엔드포인트로 파라미터에 인가 코드(Auth Code)를 포함하여서 전달합니다.


6. getAccessToken(authToken) 서비스 메서드를 호출하여 인가코드(authCode) 전달 : Controller → Service

- Controller에서는 전달받은 인가코드(Auth Code)를 기반으로 getAccessToken()이라는 서비스 메서드를 호출하여 데이터를 전달합니다.


7. accessToken 요청 : http://localhost:9001/realms/dev-realm/protocol/openid-connect/token : Service → Keycloak 관리 서버

- Spring Boot Application에서는 인가 코드(Auth Code)를 기반으로 리소스 접근 토큰(Access Token)을 반환받기 위해 Keycloak 관리서버에 호출하여 토큰 발급 요청을 수행합니다.


8. 접근 토큰(Access Token) 반환 : Keycloak 관리 서버 → Service
- 인가 코드(Auth Code)를 기반으로 접근 토큰을 발급하여 Service로 전달합니다.


9. 발급된 접근토큰 반환 : Service → Controller
- 반환받은 접근 토큰을 다시 Controller에게 반환합니다.


10. 접근 토큰(AccessToken) 반환 : Controller → Client
- 최종적으로 토큰을 사용자에게 반환하면 로그인 수행 처리가 완료가 됩니다.

 

2. 사전 Google Cloud Console OAuth 2.0 환경 설정


💡 사전 Google Cloud Console OAuth 2.0 환경 설정

- Google 로그인을 수행하기 위해서 Google Cloud Console 내에 환경 설정이 필요합니다.
- 해당 설정은 아래의 글을 참고하시면 도움이 됩니다.

https://adjh54.tistory.com/657

 

 

 

3) Keycloak Identity providers 설정


 

1. Keycloak Realm > Identity providers > Google을 선택합니다.


 

 

 

2. Add Google provider 내에 각각 속성을 입력하고 ‘Add‘ 버튼을 눌러줍니다.


속성 필수 여부 설명
Redirect URI 선택 Google 로그인 후 리다이렉트될 Keycloak의 콜백 URL
Alias * 필수 Identity Provider의 고유 식별자 (필수)
Display name 선택 로그인 화면에 표시될 이름
Client ID * 필수 Google Cloud Console에서 발급받은 클라이언트 ID (필수)
Client Secret * 필수 Google Cloud Console에서 발급받은 클라이언트 시크릿 (필수)
Display order 선택 여러 소셜 로그인 버튼이 있을 때의 표시 순서
Prompt 선택 Google 로그인 시 사용자에게 표시할 동의 화면 설정
Hosted Domain 선택 특정 G Suite 도메인으로 로그인을 제한할 때 사용
Use userIp param 선택 사용자 IP를 Google에 전송할지 여부
Request refresh token 선택 리프레시 토큰 요청 여부 설정

 

 

3. Advanced settings 탭으로 이동하여 ‘Store tokens’ 옵션을 선택하고 ‘Save’ 버튼을 누릅니다.


설정 설명
Store tokens 토큰을 저장할지 여부를 설정
Accepts prompt=none forward from client 클라이언트로부터 prompt=none 파라미터를 수락할지 여부
Disable user info 사용자 정보 엔드포인트 사용을 비활성화할지 여부
Trust Email Google에서 제공하는 이메일을 신뢰할지 여부
Account linking only 계정 연동만 허용할지 여부
Hide on login page 로그인 페이지에서 Google 로그인 옵션을 숨길지 여부
Verify essential claim 필수 클레임 검증 여부
First login flow override 첫 로그인 시 사용할 인증 플로우 재정의
Post login flow 로그인 후 실행할 추가 플로우 설정
Sync mode 사용자 데이터 동기화 모드 설정 (Legacy/Force/Import)
Case-sensitive username 사용자명 대소문자 구분 여부

 

 

[ 더 알아보기 ]

💡 Store tokens 옵션을 선택해야 하는 이유는?


1. 토큰 재사용: 사용자가 다시 인증할 필요 없이 Google API에 접근할 수 있습니다.
2. 세션 관리: 사용자의 소셜 로그인 세션을 효과적으로 관리할 수 있습니다.
3. 토큰 갱신: 리프레시 토큰을 저장하여 액세스 토큰이 만료되었을 때 자동으로 갱신할 수 있습니다.
4. 사용자 경험: 잦은 재인증 없이 원활한 서비스 이용이 가능합니다.

 

 

4) Keycloak Social 로그인 구현


 

1. 지정된 Realm의 endpoint로 접속을 해봅니다.


💡 지정된 Realm의 endpoint로 접속을 해봅니다

- 이 엔드포인트는 OAuth 2.0/OpenID Connect의 Authorization Code Flow에서 사용되는 인증 엔드포인트입니다. 사용자를 인증하고 권한을 부여받기 위한 첫 단계로 사용됩니다.
- 인증 코드(Authentication Code)를 먼저 발급받은 후, 이를 통해 액세스 토큰을 얻는 2단계 인증과정에서 사용합니다.
// format
// [GET] /realms/{realm-name}/protocol/openid-connect/auth 
 
// example
// http://localhost:9001/realms/dev-realm/protocol/openid-connect/auth?client_id=spring-boot-app&response_type=code&redirect_uri=http://localhost:8080/api/v1/keycloak/callback

 

 

 

💡 해당 과정은 아래의 과정을 처리합니다.

1. Keycloak URL 접근 및 Google 로그인 수행: Client → Keycloak 관리 서버


- 최초 클라이언트는 Keycloak 로그인 페이지로 접근을 합니다. 해당 접근 시, 최종 리턴이 되는 Spring Boot Application의 엔드포인트 redirect_uri를 함께 파라미터에 담아서 전송합니다.

- 접속 URL 형태 : /reams/{realm-name}/protocol/openid-connect/auth?client_id={keyclaokClient}?response_type=code&redirect_uri={applicationRedirectUri}
- 사용한 URL : http://localhost:9001/realms/dev-realm/protocol/openid-connect/auth?client_id=spring-boot-app&response_type=code&redirect_uri=http://localhost:8080/api/v1/keycloak/callback

 

 

 

2. keycloak 내의 Google Login이 추가되어서 출력되었습니다.


 

 

 💡 해당 과정은 아래의 과정을 처리합니다.

2. Google 로그인 아이콘을 누른 경우, 로그인 페이지를 제공 : Keycloak 로그인 화면 → Google

- Keycloak 로그인 화면에서 Google 로그인 아이콘을 누르면 Google Login 페이지를 제공합니다.

 

 

3. 구글 로그인 수행


💡 구글 로그인 수행

- 해당 클라이언트(Spring-boot-app)에 사용자를 로그인합니다.

 

 

💡 해당 과정은 아래의 과정을 처리합니다.

3. Google 로그인 아이콘을 누른 경우, 로그인 페이지를 제공 : Keycloak 로그인 화면 → Google
- Keycloak 로그인 화면에서 Google 로그인 아이콘을 누르면 Google Login 페이지를 제공합니다.

 

4. [참고] 아래와 같이 400 오류: redirect_uri_mismatch가 발생하였습니다.


💡 [참고] 아래와 같이 400 오류: redirect_uri_mismatch가 발생하였습니다.

- 아래와 같은 오류는 Google Cloud Console 내에서 redirect url을 keycloak redirect로 입력하지 않은 오류입니다.
- 처리과정은 사용자 -> Keycloak -> Google -> Keycloak -> 클라이언트 애플리케이션 와 같은 형태로 처리가 됩니다.
- 그렇기에 해당 과정에서 Google에서 keycloak로 리다이렉트 되어서 클라이언트 애플리케이션으로 리다이렉트 되는 구조로 구성되어야 합니다.

 

💡 [참고] 수정은 아래의 Google Cloud > 애플리케이션 선택에서 변경이 가능합니다.

- 아래의 승인된 리다이렉션 URL에 Keycloak에서 발급받은 엔드포인트로 호출을 합니다.

 

 

 

5. 로그인 성공 후 Keycloak 리다이렉트 URL로 반환합니다.


 💡 로그인 성공 후 Keycloak 리다이렉트 URL로 반환합니다.

- 아래의 Google Cloud 내에서 지정한 승인된 리다이렉션 URI로 리턴을 하게 됩니다.

 

 

💡 위에 리다이렉션을 통해 Keycloak의 Google Provider에게 전달이 되고 지정한 엔드포인트로 반환이 됩니다.

- 즉, Google → Keycloak으로 반환이 되는 과정입니다.

 

 

💡 해당 과정은 아래의 과정을 처리합니다.

5. Google 로그인 성공 후 Keycloak 리다이렉트 URL로 반환해 줍니다. : Google → Keycloak 관리 서버


- Google에서 지정한 redirect_uri를 기반으로 Keycloak 관리서버로 리다이렉트 됩니다.
- 구성한 URL: http://localhost:9001/realms/dev-realm/broker/google/endpoint

 

5. Keycloak에서 redirect_uri를 지정한 엔드포인트로 리턴을 하여 인가 코드(Authentication Code)를 전달받습니다.


💡 Keycloak에서 redirect_uri를 지정한 엔드포인트로 리턴을 하여 인가 코드(Authentication Code)를 전달받습니다.

- 해당 과정은 Google → Keycloak → Appliction으로 전달받는 과정으로 최초 클라이언트가 파라미터로 전달한 redirect_uri 엔드포인트로 Controller에게 전달이 됩니다.

 

 💡 최초 로그인 페이지가 출력되는 단계에서 redirect_uri를 입력하였습니다.

- 구글 로그인 수행이 완료된 다음에 해당 uri로 리다이렉트 되는 과정입니다.

 

💡 KeycloakController

- 아래는 일반 사용자 로그인과 동일한 API를 사용합니다. 그렇기에 OAuth 2.0용을 위한 Controller의 구성없이 사용하였습니다.
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/keycloak")
public class KeycloakController {

    private final KeycloakService keycloakService;
    private final KeycloakProperties keycloakProperties;

    /**
     * Keycloak 로그인 성공 이후 정보 수신 리다이렉트 URL
     *
     * @param code
     * @param state
     * @param session_state
     * @param error
     * @param error_description
     * @return
     */
    @GetMapping("/callback")
    public ResponseEntity<Object> loginCallback(
            @RequestParam(required = false) String code,
            @RequestParam(required = false) String state,
            @RequestParam(required = false) String session_state,
            @RequestParam(required = false) String error,
            @RequestParam(required = false) String error_description
    ) {

        log.debug("code :: {}", code);
        log.debug("state :: {}", state);
        log.debug("session_state :: {}", session_state);
        log.debug("error :: {}", error);
        log.debug("error_description :: {}", error_description);

        return new ResponseEntity<>(code, HttpStatus.OK);
    }
}

 

 💡 Application 내에서 아래와 같이 code라는 값으로 인가 코드(Authentication Code)를 반환받았습니다.

 

 💡 해당 과정은 아래의 과정을 처리합니다.

5. Keycloak에서는 최초 클라이언트가 파라미터로 전달한 redirect_uri 엔드포인트로 리턴하며 반환 값으로 인가 코드(Auth Code)를 전달 : Keycloak 관리 서버 → KeycloakController

- Keycloak에서는 최초 클라이언트가 파라미터로 함께 보냈던 redirect_uri 엔드포인트로 파라미터에 인가 코드(Auth Code)를 포함하여서 전달합니다

 

 

6. getAccessToken(authToken) 서비스 메서드를 호출하여 인가코드(authCode) 전달합니다.


💡 getAccessToken(authToken) 서비스 메서드를 호출하여 인가코드(authCode) 전달

- 해당 과정에서 Controller에서 전달받은 인가코드(Authentication Code)를 기반으로 접근 토큰(Access Token)을 반환받기 위해 서비스의 getAccessToken() 메서드를 호출합니다.

 

6.1. KeycloakController


💡 KeycloakController

- 주요한 값을 추가하여서 함께 getAccessToken()를 호출합니다.
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/keycloak")
public class KeycloakController {

    private final KeycloakService keycloakService;
    private final KeycloakProperties keycloakProperties;

    /**
     * Keycloak 로그인 성공 이후 정보 수신 리다이렉트 URL
     *
     * @param code
     * @param state
     * @param session_state
     * @param error
     * @param error_description
     * @return
     */
    @GetMapping("/callback")
    public ResponseEntity<Object> loginCallback(
            @RequestParam(required = false) String code,
            @RequestParam(required = false) String state,
            @RequestParam(required = false) String session_state,
            @RequestParam(required = false) String error,
            @RequestParam(required = false) String error_description
    ) {

        log.debug("code :: {}", code);
        log.debug("state :: {}", state);
        log.debug("session_state :: {}", session_state);
        log.debug("error :: {}", error);
        log.debug("error_description :: {}", error_description);

        AccessTokenReqDto accessTokenReqDto = AccessTokenReqDto.builder()
                .grant_type("authorization_code")
                .client_id(keycloakProperties.getResource())
                .client_secret(keycloakProperties.getCredentials().getSecret())
                .code(code)
                .redirect_uri(keycloakProperties.getRedirectUrl())
                .build();

        log.debug("전송하려는 데이터 : {}", accessTokenReqDto);

        AccessTokenResDto result = keycloakService.getAccessToken(accessTokenReqDto);

        return new ResponseEntity<>(code, HttpStatus.OK);
    }

 

 

6.2. AccessTokenReqDto


💡 AccessTokenReqDto

- Access Token 발급 요청 객체입니다.
package com.blog.springbootkeycloak.dto;

import lombok.*;

/**
 * Access Token 발급 요청 객체
 *
 * @author : jonghoon
 * @fileName : AccessTokenReqDto
 * @since : 25. 1. 25.
 */
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AccessTokenReqDto {

    private String code;
    private String grant_type;
    private String client_id;
    private String client_secret;
    private String username;
    private String password;
    private String redirect_uri;

    @Builder
    public AccessTokenReqDto(String code, String grant_type, String client_id, String client_secret, String username, String password, String redirect_uri) {
        this.code = code;
        this.grant_type = grant_type;
        this.client_id = client_id;
        this.client_secret = client_secret;
        this.username = username;
        this.password = password;
        this.redirect_uri = redirect_uri;
    }
}

 

 

6.3. AccessTokenResDto


💡 AccessTokenResDto

- Access Token 발급 응답 객체입니다.
package com.blog.springbootkeycloak.dto;

import lombok.*;

/**
 * Access Token 발급 응답 객체
 *
 * @author : jonghoon
 * @fileName : TokenResponseDto
 * @since : 25. 1. 28.
 */
@ToString
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AccessTokenResDto {
    private String access_token;
    private int expires_in;
    private int refresh_expires_in;
    private String refresh_token;
    private String token_type;
    private int not_before_policy;
    private String session_state;
    private String scope;

    @Builder
    public AccessTokenResDto(String access_token, int expires_in, int refresh_expires_in, String refresh_token, String token_type, int not_before_policy, String session_state, String scope) {
        this.access_token = access_token;
        this.expires_in = expires_in;
        this.refresh_expires_in = refresh_expires_in;
        this.refresh_token = refresh_token;
        this.token_type = token_type;
        this.not_before_policy = not_before_policy;
        this.session_state = session_state;
        this.scope = scope;
    }
}

 

 

6.4. KeycloakService


💡 KeycloakService

- OpenFeign를 이용하여서 Keycloak에 호출을 하여서 접근 토큰을 발급하는 과정을 구현하는 부분입니다.
package com.blog.springbootkeycloak.service;

import com.blog.springbootkeycloak.dto.AccessTokenResDto;
import com.blog.springbootkeycloak.dto.AuthCodeDto;
import com.blog.springbootkeycloak.dto.AccessTokenReqDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

/**
 * OpenFeign 통해서 Keycloak 서비스 통신을 수행합니다.
 *
 * @author : jonghoon
 * @fileName : OpenFeignService
 * @since : 11/23/24
 */
@FeignClient(
        name = "keycloak-auth-service",
        url = "<http://localhost:9001/realms/dev-realm/protocol/openid-connect>"
)
@Service
public interface KeycloakService {

    /**
     * 접근 토큰(Access Token) 발급
     *
     * @param accessTokenReqDto 전송 객체 값 (form 데이터 형태로 전송)
     * @return 토큰 값 반환
     */
    @PostMapping(value = "/token", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    AccessTokenResDto getAccessToken(@ModelAttribute AccessTokenReqDto accessTokenReqDto);
}

 

💡 위에 구성을 통해 최종적으로 아래와 같이 access_token을 반환함을 확인하였습니다.

 

💡 해당 과정은 아래의 과정을 처리합니다.

7. accessToken 요청 : http://localhost:9001/realms/dev-realm/protocol/openid-connect/token : Service → Keycloak 관리 서버

- Spring Boot Application에서는 인가 코드(Auth Code)를 기반으로 리소스 접근 토큰(Access Token)을 반환받기 위해 Keycloak 관리서버에 호출하여 토큰 발급 요청을 수행합니다.

8. 접근 토큰(Access Token) 반환 : Keycloak 관리 서버 → Service
- 인가 코드(Auth Code)를 기반으로 접근 토큰을 발급하여 Service로 전달합니다.

9. 발급된 접근토큰 반환 : Service → Controller
- 반환받은 접근 토큰을 다시 Controller에게 반환합니다.

10. 접근 토큰(AccessToken) 반환 : Controller → Client
- 최종적으로 토큰을 사용자에게 반환하면 로그인 수행 처리가 완료가 됩니다.

 

 

 

5) 결과 확인


💡 결과 확인

- Google Login을 통해 로그인이 수행되고, Access Token이 생성되는 과정이였습니다.
- Keycloak에 접속하여서 확인을 하면 사용자로 추가가 됨을 확인할 수 있습니다.

 

 

💡 해당 사용자를 선택하고 Identity provider links 탭으로 이동하면 Google Social Login이 됨을 확인할 수 있습니다.

 

 

 

오늘도 감사합니다 😀

 

그리드형