SSO는 사용자가 한 번의 로그인만으로 여러 시스템이나 서비스에 접근할 수 있게 해주는 인증 방법입니다. 예를 들어, 사용자가 한 번 로그인을 하면 Gmail, Youtube, Gooble Drive 같은 여러 구글 서비스에 별도의 로그인 없이 접근할 수 있습니다.
1. 사용자 편의성 향상
한 번 로그인으로 여러 시스템에 접근할 수 있어 비밀번호를 여러 번 입력하는 불편을 줄입니다.
2. 보안 강화
사용자는 여러 계정과 비밀번호를 기억할 필요가 없기 때문에, 비밀번호를 재사용하거나 보안이 낮은 비밀번호를 설정하는 위험을 줄일 수 있습니다. 또한 SSO를 통해 중앙에서 보안 관리 및 모니터링이 가능해집니다.
3. 관리 효율성
기업에서는 사용자의 계정 관리를 중앙 집중적으로 할 수 있어, 사용자 계정 생성, 변경, 삭제 등의 작업을 쉽게 수행할 수 있습니다.
4. 다양한 인증 방식 지원
SSO는 OAuth, SAML(Security Assertion Markup Language), OpenID Connect와 같은 프로토콜을 사용하여 구현될 수 있습니다. 이들 프로토콜은 서로 다른 애플리케이션이나 시스템 간의 인증 정보를 안전하게 주고받는 방식을 정의합니다.
SSO(Single Sign-On)을 구현하기 위해서는 두 가지 요소가 필요합니다. SSO 서버와 클라이언트 애플리케이션 이를 통해 사용자가 한 번 로그인하면 여러 애플리케이션에서 인증이 유지됩니다.
SSO를 구현하가 위해 선택할 수 있는 인증 프로토콜이 몇가지 있습니다.
1. OAuth 2.0
권한 위임 프로토콜로, 주로 API 및 서드파티 애플리케이션에서 많이 사용됩니다.
2. OpenID Connect(OIDC)
OAuth 2.0의 확장으로, 인증 기능을 제공하며 로그인 처리를 쉽게 구현할 수 있습니다.
3. SAML(Security Assertion Markup Language)
XML 기반의 프로토콜로, 주로 엔터프라이즈 환경에서 많이 사용됩니다.
SSO 서버는 인증과 관련된 모든 로직을 처리하며, 일반적으로 사용자 인증 토큰을 발급하고 이를 클라이언트가 사용할 수 있도록 해줍니다. 아래의 SSO 서버 설정 방법은 OAuth 2.0과 OpenID Connect에 기반을 둡니다.
1. SSO 서버 선택 : 직접 구현하거나, 이미 개발된 SSO 솔루션을 사용
2. SSO 서버 설정
3. OAuth 2.0 / OpenID Connect 플로우 설정
Authorization Code Flow : 가장 많이 사용되는 플로우로 클라이언트가 인증 코드르 받고, 서버에서 토큰을 교환
SSO 서버는 인증 후 사용자에게 JWT(JSON Web Token) 또는 액세스 토큰을 발급합니다.
1. 로그인 페이지 구현
사용자가 로그인 버튼을 누르면 클라이언트는 SSO 서버로 Authorization Code를 요청하는 리퀘스트를 보냅니다.
GET https://sso-server.com/auth?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&response_type=code&scope=openid profile email
2. Authorization Code 수신 및 토큰 요청
사용자가 SSO 서버에서 인증에 성공하면 Authorization Code가 리다이렉트 URL을 통해 클라이언트 애플리케이션에 전달됩니다.
클라이언트는 이 코드를 사용해 SSO 서버에 Access Token과 ID Token을 요청합니다.
POST https://sso-server.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
code={AUTHORIZATION_CODE}
redirect_uri={REDIRECT_URI}
client_id={CLIENT_ID}
client_secret={CLIENT_SECRET}
3. 토큰 검증
SSO 서버는 클라이언트에게 Access Token과 ID Token(JWT)를 발급합니다. 클라이언트는 JWT를 디코딩하고 검증하여 사용자 정보를 확인할 수 있습니다.
예를 들어 JWT 토큰을 파싱하고, 사용자 프로필 정보를 가져와 세션을 관리할 수 있습니다.
4. 세션 유지 및 로그아웃 처리
클라이언트 애플리케이션은 발급된 Access Token을 사용해 보호된 API에 접근합니다.
로그아웃을 구현하려면 SSO 서버와 통신하여 세션을 무효화해야 합니다.
GET https://sso-server.com/logout?redirect_uri={REDIRECT_URI}
5. SSO 클라이언트 연동
다양한 클라이언트 애플리케이션에서 SSO를 연동할 수 있습니다. 대부분의 프로그래밍 언어에서 OAuth 2.0 및 OpenID Connect를 지원하는 라이브러리들이 제공됩니다.
1. Keycloak 설정
Keycloak 관리 콘솔에서 새로운 클라이언트를 등록하고, 클라이언트 ID,리다이렉트 URL을 설정합니다.
2. 클라이언트 애플리케이션에서 Keycloak을 통한 인증 요청
const keycloak = new Keycloak({
url: 'https://keycloak-server.com',
realm: 'myrealm',
clientId: 'myclient',
});
keycloak.init({ onLoad: 'login-required' }).success(function() {
console.log('Logged in');
}).error(function() {
console.log('Failed to login');
});
1. 필요 패키지 설치 - Go에서는 위에서 언급한 것처럼
golang.org/x/oauth2 패키지를 사용
go get golang.org/x/oauth2
go get golang.org/x/oauth2/github
2. 환경설정
OAuth 2.0 클라이언트를 사용하기 위해서는 서비스 제공자(GitHub, Google 등)에서 클라이언트 ID와 클라이언트 비밀키를 발급받아야 합니다. GitHub의 경우 GitHub OAuth 앱 설정 페이지에서 애플리케이션을 등록할 수 있습니다.
아래 링크에 접속하여 새로운 OAuth application 등록을 진행합니다.
https://github.com/settings/applications/new
위 화면에서 Homepage URL은 테스트 환경이라면 http://localhost:8080
Authorization callback URL은 http://localhost:8080/callback 으로 작성합니다.
애플리케이션을 정상 등록하면 위 화면에서 클라이언트 ID와 비밀키를 확인할 수 있습니다.
.env 파일 내 아래 처럼 작성
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
3. OAuth 2.0 클라이언트 구현
아래 코드를 main.go 파일에 작성
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"github.com/joho/godotenv"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
)
var (
// GitHub OAuth 2.0 설정
githubOauthConfig *oauth2.Config
// 무작위 문자열 생성, CSRF 방지용
oauthStateString = "random"
)
func init() {
// .env 파일을 로드하여 환경 변수를 설정
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file")
}
// 환경 변수 값 로그 출력
clientID := os.Getenv("GITHUB_CLIENT_ID")
clientSecret := os.Getenv("GITHUB_CLIENT_SECRET")
log.Print("env loaded - CLIENT_ID: ", clientID)
log.Print("env loaded - CLIENT_SECRET: ", clientSecret)
// OAuth 설정 초기화
githubOauthConfig = &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: "http://localhost:8080/callback", // GitHub OAuth 앱에 설정된 리다이렉트 URL과 일치해야 함
Scopes: []string{"user:email"},
Endpoint: github.Endpoint,
}
// OAuth 설정 로그 출력
log.Printf("OAuth ClientID: %s\n", githubOauthConfig.ClientID)
log.Printf("OAuth ClientSecret: %s\n", githubOauthConfig.ClientSecret)
}
// 메인 핸들러
func handleMain(w http.ResponseWriter, r *http.Request) {
var htmlIndex = `<html><body><a href="/login">GitHub로 로그인하기</a></body></html>`
fmt.Fprintf(w, htmlIndex)
}
// 로그인 핸들러
func handleGitHubLogin(w http.ResponseWriter, r *http.Request) {
// GitHub로 리다이렉트 (OAuth 2.0 Authorization Code Flow 시작)
url := githubOauthConfig.AuthCodeURL(oauthStateString)
log.Printf("Redirecting to: %s\n", url) // 리다이렉트 URL 로그로 출력
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
// 콜백 핸들러
func handleGitHubCallback(w http.ResponseWriter, r *http.Request) {
// CSRF 방지용 state 체크
state := r.FormValue("state")
if state != oauthStateString {
log.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
// Authorization Code 수신
code := r.FormValue("code")
token, err := githubOauthConfig.Exchange(context.Background(), code)
if err != nil {
log.Printf("oauthConf.Exchange() failed with '%s'\n", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
// GitHub API를 사용하여 사용자 정보 가져오기
response, err := http.Get("https://api.github.com/user?access_token=" + token.AccessToken)
if err != nil {
log.Printf("Get: %s\n", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
defer response.Body.Close()
// 사용자 정보 디코딩 및 출력
var user map[string]interface{}
json.NewDecoder(response.Body).Decode(&user)
fmt.Fprintf(w, "UserInfo: %v\n", user)
}
func main() {
http.HandleFunc("/", handleMain)
http.HandleFunc("/login", handleGitHubLogin)
http.HandleFunc("/callback", handleGitHubCallback)
fmt.Println("Started running on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
이후 서버를 실행해서 테스트 할 수 있습니다.
go run main.go
1. 브라우저에서 http://localhost:8080에 접속
2. GitHub로 로그인하기 링크를 클릭하면 GitHub 로그인 페이지로 리다이렉트됩니다.
3. 로그인에 성공하면, GitHub에서 사용자 정보를 받아와 브라우저에 출력합니다.
이러한 패턴으로 SSO 구현 테스트를 진행할 수 있습니다.
GitHub로 로그인 하기 버튼 클릭 후 GitHub 페이지에서 404 화면이 나온다면 발급받은 Client_id와 비밀키를 다시 확인하시기 바랍니다.