第八章:OpenID Connect 身份认证

“OAuth 2.0 告诉你’这个人授权了’,OIDC 告诉你’这个人是谁’。”

        mindmap
  root((OpenID Connect))
    核心概念
      ID Token
      UserInfo
      Claims
    流程
      Authorization Code
      Implicit
      Hybrid
    发现
      Discovery
      JWKS
    提供商
      Keycloak
      Auth0
      Okta
    

8.1 OIDC 与 OAuth 2.0 的关系

OIDC(OpenID Connect)是建立在 OAuth 2.0 之上的身份认证层

┌─────────────────────────────────────┐
│         OpenID Connect              │
│    (身份认证 — 你是谁?)             │
│    ID Token, UserInfo, Claims       │
├─────────────────────────────────────┤
│           OAuth 2.0                 │
│    (授权 — 你能做什么?)             │
│    Access Token, Scope, Grant       │
├─────────────────────────────────────┤
│           HTTP / TLS                │
└─────────────────────────────────────┘

维度

OAuth 2.0

OIDC

目的

授权(访问资源)

认证(确认身份)

核心 Token

Access Token

ID Token

用户信息

不标准

UserInfo Endpoint

发现机制

.well-known/openid-configuration

Scope

自定义

openid, profile, email

8.2 ID Token

ID Token 是一个 JWT,包含用户身份信息:

{
  "iss": "https://auth.example.com",
  "sub": "user-123",
  "aud": "my-client-app",
  "exp": 1709510400,
  "iat": 1709506800,
  "nonce": "abc123",
  "name": "Walter Fan",
  "email": "walter@example.com",
  "email_verified": true,
  "picture": "https://example.com/photo.jpg"
}

标准 Claims

Claim

说明

必须

iss

签发者

sub

用户唯一标识

aud

受众(Client ID)

exp

过期时间

iat

签发时间

nonce

防重放攻击

条件必须

name

用户全名

email

邮箱

picture

头像 URL

8.3 OIDC 授权码流程

┌──────────┐                    ┌──────────────┐
│  用户     │  1. 访问应用       │              │
│ (浏览器)  │──────────────────▶│   Client     │
│          │                    │  (Web App)   │
│          │  2. 重定向到 IdP   │              │
│          │◀──────────────────│              │
│          │                    └──────────────┘
│          │  3. 登录 + 授权
│          │──────────────────▶ ┌──────────────┐
│          │                    │     IdP      │
│          │  4. 重定向回 Client│ (OIDC Provider)│
│          │     + auth code   │              │
│          │◀──────────────────│              │
│          │                    └──────┬───────┘
│          │                           │
└────┬─────┘                           │
     │                                 │
     ▼                                 │
┌──────────────┐  5. code → tokens     │
│   Client     │──────────────────────▶│
│  (后端)      │  6. id_token +        │
│              │     access_token      │
│              │◀──────────────────────│
│              │                       │
│              │  7. 验证 id_token     │
│              │  8. 获取 UserInfo     │
│              │──────────────────────▶│
│              │  9. 用户信息          │
│              │◀──────────────────────│
└──────────────┘

8.4 OIDC Discovery

每个 OIDC 提供商都有一个发现端点:

GET https://auth.example.com/.well-known/openid-configuration

{
  "issuer": "https://auth.example.com",
  "authorization_endpoint": "https://auth.example.com/authorize",
  "token_endpoint": "https://auth.example.com/oauth/token",
  "userinfo_endpoint": "https://auth.example.com/userinfo",
  "jwks_uri": "https://auth.example.com/.well-known/jwks.json",
  "scopes_supported": ["openid", "profile", "email"],
  "response_types_supported": ["code", "id_token", "token id_token"],
  "id_token_signing_alg_values_supported": ["RS256", "ES256"],
  "subject_types_supported": ["public", "pairwise"]
}

8.5 OIDC 提供商对比

特性

Keycloak

Auth0

Okta

Google

类型

开源自托管

SaaS

SaaS

SaaS

协议

OIDC/SAML/OAuth2

OIDC/SAML

OIDC/SAML

OIDC

定价

免费

免费层+付费

付费

免费

自定义

企业功能

完整

完整

完整

有限

部署

自托管/K8s

8.6 Python OIDC 客户端

from authlib.integrations.starlette_client import OAuth
from starlette.config import Config
from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="session-secret")

oauth = OAuth()
oauth.register(
    name='keycloak',
    server_metadata_url='https://auth.example.com/realms/myrealm/.well-known/openid-configuration',
    client_id='my-app',
    client_secret='my-secret',
    client_kwargs={'scope': 'openid profile email'},
)

@app.get('/login')
async def login(request: Request):
    redirect_uri = request.url_for('auth_callback')
    return await oauth.keycloak.authorize_redirect(request, redirect_uri)

@app.get('/callback')
async def auth_callback(request: Request):
    token = await oauth.keycloak.authorize_access_token(request)
    id_token = token.get('id_token')
    userinfo = token.get('userinfo')
    
    # 用户已认证
    return {
        "sub": userinfo["sub"],
        "name": userinfo.get("name"),
        "email": userinfo.get("email"),
    }

8.7 小结

  • OIDC 是 OAuth 2.0 之上的身份认证层,提供标准化的用户身份信息

  • ID Token 是 JWT 格式,包含用户身份 Claims

  • Discovery 端点使客户端可以自动发现 OIDC 配置

  • Keycloak 是最流行的开源 OIDC 提供商,适合自托管场景

  • OIDC 是零信任架构中人类身份认证的首选协议