한 번의 로그인 인증으로 여러 개의 서비스나 애플리케이션에 접근할 수 있게 해주는 인증 매커니즘

이미지 출처: https://www.indusface.com/blog/what-is-single-sign-on/
대표적인 SSO 업체로는 AWS, Google Workspace 등이 있다.
사용자가 서비스에 접근 시도
인증되지 않았으면, 서비스는 사용자를 SSO 서버(IdP)로 리디렉션
사용자가 IdP(Id Provider)에서 로그인
IdP는 인증 후 토큰(예: SAML, OAuth, JWT 등) 을 생성하여 서비스로 전달
서비스는 이 토큰을 검증하고 사용자에게 접근 권한 부여
예시: Google 계정으로 Gmail, Google Drive, YouTube 모두 로그인 없이 이용 가능
SAML은 인증 정보 제공자와 서비스 제공자 간의 인증 및 인가 데이터를 교환하기 위한 XML기반의 표준 데이터 포맷이다.
SAML은 인증정보를 XML포맷으로 생성하고, 이 XML데이터를 암호화함으로써 제 3자에게 내용을 노출시키지 않고 최종 수신자에게 전달할 수 있다.
데이터가 XML 형식이고, 설정 난이도도 높으며, REST API가 아니라 브라우저를 매개로 동작하는 HTTP Redirection 등의 방식을 사용한다.
React같은 기술을 사용한 SPA나 모바일 환경 등에 적합하지 않으며, 확장성도 떨어진다.
인증 서버가 해당 도메인의 인증을 담당하고, 다른 도메인에 권한만 부여하는 방식.
로그인이 필요할 때마다 인증 페이지로 redirect한다.
→ 인증이 성공하면, 임의의 문자열인 Authorization Code를 발급하고 쿼리스트링에 포함하여 원래의 페이지로 redirect한다. (이 때, Cookie나 Redis 등을 사용하여 짧은 시간동안 Authorization Code를 저장해둔다. + 세션 쿠키 or Refresh Token을 발급하여 브라우저에 함께 저장한다.)
// 인증 페이지로 redirect 예시
http://localhost:3000/signin?redirect_uri=http://localhost:3001/callback
// 인증 성공 시 redirect 예시
http://localhost:3001/callback?code=a1s2d3f4
→ 클라이언트는 이 Authorization Code를 추출해서, 다시 인증 서버로 HTTP POST 요청을 보낸다. (Authorization Code를 Request의 body로 보냄)
// post 요청 예시
await fetch('http://localhost:3000/oauth/token', {
method: 'POST',
code: authCode
})
→ 인증 서버에서 POST 요청을 통해서 받은 Authorization Code를 이전에 저장해둔 Code와 비교해서 일치한다면, access_token(JWT)을 발급하여 Response의 body로 보낸다.
→ 클라이언트는 전달받은 access_token(JWT)을 쿠키, LocalStorage 등에 저장하고, HTTP 요청 시 header의 Authorization: Bearer <Token> 으로 보낸다.
// access_token에 포함되는 정보
{
"iss": "https://example.com", // 토큰 발급자(서버)
"sub": "1234567890", // 사용자 식별자(사번, id 등)
"aud": "https://api.example.com", // 토큰의 수신자(클라이언트)
"exp": 1516239022, // 토큰의 만료 시간
"scope": "read write" // 부여된 권한
}
OIDC(Open ID Connect): OAuth2.0 방식에 id_token을 추가한 방식.
OAuth는 인증(Authentication)이 아닌 인가(Authorization)을 위한 프로토콜이다.
하지만, 사용자가 로그인되어 있는지 여부가 중요하다면 서버에서 클라이언트로 id_token을 전달하여, 현재 사용자가 로그인된 사용자라는 사실 및 그 사용자 정보를 알 수 있도록 하는 OIDC 방식을 사용할 수 있다.
// id_token에 포함되는 정보
{
"iss": "https://example.com",
"sub": "1234567890",
"aud": "https://app.example.com",
"exp": 1516239022,
"iat": 1516235422, // 토큰 발급 시간
"auth_time": 1516235000, // 사용자 인증 시간
"nonce": "abcdef", // 무작위 문자열, 재생 공격 방지용
"at_hash": "XYZ123", // Access Token 토큰 해시
"email": "user@example.com", // 사용자의 이메일
"name": "John Doe", // 사용자의 이름
"picture": "https://example.com/profile.jpg" // 사용자 프로필 사진 URL
}
id_token에는 위와 같이 사용자 정보와 보안을 위한 정보들이 포함된다.
access_token은 api에 접근하여 사용할 수 있는 권한을 위해 주로 클라이언트→서버로 보내는 토큰이라면,
id_token은 사용자의 정보 및 로그인 여부를 클라이언트에게 안전하게 전달하기 위해 서버→클라이언트로 보내는 토큰이다.
access_token은 서버에서 api를 사용할 때 session을 사용하여 검증한다면 토큰에 sessionID만 보내면 되기 때문에, JWT를 사용하는 것은 선택이지만,
id_token은 클라이언트 측에서 위변조 여부를 확인할 수 있어야 하기 때문에 거의 필수적으로 JWT를 사용한다.