본문 바로가기
C.W.K.
Stream
Lesson 03 of 05 · published

OAuth, Magic Link, 다중 사용자 onboarding

~15 min · oauth, magic-links, allowlist

Level 0Greenhorn
0 XP0/53 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

두 번째 사람이 앱에 들어오는 순간 identity 관리의 영역으로 넘어온 거야. 여기서부터는 패턴이 더 전문화되고.

흔한 세 패턴

패턴적합마찰
OAuth (Google/GitHub/Apple 로 sign in)그 provider 중 하나에 stable identity 이미 있는 사용자제일 낮아. 클릭 한 번이면 돼. 대신 IdP 에 묶여
Magic link (이메일)비밀번호 없이 per-user 계정 원하는 솔로 개발자낮아. 이메일 치고 링크 누르면 끝. 대신 이메일이 제때 오는지에 기대야 해
Passkey-only signup기술 편한 사용자 베이스, 모던 디바이스중간. 'passkey 만들기' 는 아직 낯선 UX 야

Magic link 패턴 (가장 싼 per-user 솔루션)

장점은 비밀번호가 아예 없고, 누가 했는지가 남고, 복구가 그냥 "magic link 다시 보내기" 라는 거야. 단점은 이메일 보안에 통째로 기댄다는 것. 사용자의 이메일 비밀번호가 곧 네 앱의 자격증명이 돼버려.

Code

Magic link 발급·python
# 1. 사용자 이메일 입력
# 2. 서버가 one-time 토큰 생성
token = secrets.token_urlsafe(32)
db.execute(
    "INSERT INTO magic_links(email, token, expires_at) VALUES (?,?,?)",
    (email, token, now() + 900)  # 15분
)
# 3. 링크 이메일: https://app/auth/magic?token=<token>
# 4. 사용자가 클릭하면 서버가 토큰 검증 → expire → session 쿠키 발급
authlib + FastAPI 로 OAuth·python
from authlib.integrations.starlette_client import OAuth

oauth = OAuth()
oauth.register(
    name="google",
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],  # keychain 에서 가져와. .env 에 두지 마
    server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
    client_kwargs={"scope": "openid email profile"},
)

@app.get("/auth/google")
async def google_login(request: Request):
    return await oauth.google.authorize_redirect(request, "https://app/auth/google/callback")

@app.get("/auth/google/callback")
async def google_callback(request: Request):
    token = await oauth.google.authorize_access_token(request)
    userinfo = token["userinfo"]
    user = upsert_user(email=userinfo["email"], name=userinfo["name"])
    issue_session_cookie(user)
    return RedirectResponse("/")
Allowlist — 기본 닫힘·python
ALLOWED_EMAILS = {"me@example.com", "partner@example.com", "friend@example.com"}

def upsert_user(email: str, name: str):
    if email not in ALLOWED_EMAILS:
        raise HTTPException(403, "이 앱은 등록 안 받습니다.")
    # ...

External links

Exercise

골라봐. 다음에 만들 다중 사용자 앱은 OAuth, magic link, passkey 중 뭘 쓸까? 옵션마다 한 문장씩 답을 적어. 기준은 *네 사용자* 야 (기술에 익숙한가? Google 계정이 있나? 폰을 쓰나 노트북을 쓰나?). 이 연습의 핵심은 종이 위에서 '제일 안전한 것' 을 고르는 게 아니라, 사용자의 현실에 auth 를 맞추는 거야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.