Authlib 공식 문서 - [Starlette OAuth Client]

Soobin Kim·2024년 2월 27일

공부

목록 보기
12/15

목차

Starlette OAuth Client

  1. Configuration
  2. Register Remote Apps
  3. Enable Session for OAuth 1.0
  4. Routes for Authorization
  5. Starlette OpenID Connect
  6. Examples

Starlette OAuth Client

  • Django와 Flask 프레임워크와 Starlette 기반 프레임워크의 차이점은 후자가 비동기식이라는 것이다.

  • create OAuth instance

from authlib.integrations.starlette_client import OAuth

oauth = OAuth()

1. Configuration

  • Starlette은 Starlette의 Config 클래스를 사용하여 '.env' 파일에 정의된 설정들을 로드할 수 있다(하드 코딩 방지).
  • Authlib의 OAuth 객체는 Config의 정보들을 사용하여 생성한다.
from starlette.config import Config

config = Config('.env')
oauth = OAuth(config)

# google을 등록할 때, 환경으로부터 GOOGLE_CLIENT_ID와 GOOGLE_CLIENT_SECRET을 로드하여 사용
oauth.register(name='google', ...)

2. Register Remote Apps

oauth.register(
    'google',
    client_id='...',
    client_secret='...',
    ...
)

3. Enable Session for OAuth 1.0

  • OAuth 1.0에서는 임시 자격 증명을 사용하여 액세스 토큰을 교환해야 한다.

4. Routes for Authorization

from starlette.routing import Route

# Google 로그인을 위한 라우트
async def login_via_google(request):
    google = oauth.create_client('google')
    redirect_uri = request.url_for('authorize_google')
    return await google.authorize_redirect(request, redirect_uri)

# Google 인증을 위한 라우트
async def authorize_google(request):
    google = oauth.create_client('google')
    token = await google.authorize_access_token(request)
    # 토큰 및 사용자 정보와 관련된 작업 수행
    return '...'

# Starlette 앱에 라우트 추가
routes = [
    Route('/login/google', login_via_google),
    Route('/auth/google', authorize_google)
]

5. Starlette OpenID Connect

  • Starlette OpenID Connect 클라이언트는 일반적인 OAuth 2.0 클라이언트와 다르지 않고 .register를 할 때 openid 범위(scope)를 추가하기만 하면 된다.
  • 내장된 Starlette OAuth 클라이언트가 모든 것을 자동으로 처리하므로, OpenID Connect 클라이언트를 설정하고 토큰을 받으면 자동으로 id_token을 파싱하고 userinfo를 가져올 수 있다.
oauth.register(
    'google',
    ...
    server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
    client_kwargs={'scope': 'openid profile email'}
)
  • 토큰을 받은 뒤
token = await oauth.google.authorize_access_token()
  • 응답에는 id_token이 있어야 하며 Authlib에서는 .parse_id_token을 자동으로 호출했으므로, 토큰에서 userinfo를 가져올 수 있다.
userinfo = token['userinfo']

6. Examples

import json
from starlette.config import Config
from starlette.applications import Starlette
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from authlib.integrations.starlette_client import OAuth

app = Starlette(debug=True)
app.add_middleware(SessionMiddleware, secret_key="!secret")

config = Config('.env')
oauth = OAuth(config)

CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
oauth.register(
    name='google',
    server_metadata_url=CONF_URL,
    client_kwargs={
        'scope': 'openid email profile',
        'prompt': 'select_account',  # force to select account
    }
)


@app.route('/')
async def homepage(request):
    user = request.session.get('user')
    if user:
        data = json.dumps(user)
        html = (
            f'<pre>{data}</pre>'
            '<a href="/logout">logout</a>'
        )
        return HTMLResponse(html)
    return HTMLResponse('<a href="/login">login</a>')


@app.route('/login')
async def login(request):
    redirect_uri = request.url_for('auth')
    return await oauth.google.authorize_redirect(request, redirect_uri)


@app.route('/auth')
async def auth(request):
    token = await oauth.google.authorize_access_token(request)
    user = token.get('userinfo')
    if user:
        request.session['user'] = user
    return RedirectResponse(url='/')


@app.route('/logout')
async def logout(request):
    request.session.pop('user', None)
    return RedirectResponse(url='/')


if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host='127.0.0.1', port=8000)

0개의 댓글