> Как реализована аутентификация с поддержкой OpenID и OAuth 2.0 (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: Юрент

Стек: Go

> Пример ответа

Короткий ответ

Аутентификация с OpenID Connect и OAuth 2.0 в Go обычно строится на middleware, который проверяет JWT access token, полученный от identity provider (например, Keycloak, Auth0, Dex). OAuth 2.0 отвечает за выдачу токенов, OpenID Connect добавляет ID token с информацией о пользователе. На практике используется библиотека coreos/go-oidc для верификации токенов и golang.org/x/oauth2 для authorization code flow. Middleware извлекает токен из заголовка Authorization: Bearer, проверяет подпись через JWKS, валидирует issuer, audience и expiry, затем кладёт claims в контекст запроса.

Подробное объяснение

Архитектурно решение состоит из трёх слоёв:

  1. OAuth 2.0 flow - клиент (SPA, мобильное приложение, сервис) получает access token от authorization server. Для backend-сервисов чаще используется client credentials flow, для пользовательских сценариев - authorization code flow с PKCE.

  2. OpenID Connect - поверх OAuth 2.0 добавляет ID token (JWT), который содержит стандартные claims: sub, email, name, iss, aud, exp. Backend доверяет этому токену, если он подписан ключом, опубликованным в JWKS endpoint провайдера.

  3. Верификация на стороне backend - middleware выполняет:

    • извлечение токена из заголовка;
    • проверку подписи через JWKS (алгоритмы RS256, ES256);
    • валидацию iss (issuer) и aud (audience);
    • проверку срока действия (exp, nbf);
    • опционально - проверку nonce для ID token;
    • преобразование claims в доменную модель пользователя.

Для stateless-подхода токен проверяется на каждом запросе без обращения к провайдеру. Для stateful-подхода (например, при необходимости revocation) можно добавить кэш сессий или обращение к introspection endpoint.

В Go типичная структура: oidc.Provider (получает конфигурацию и JWKS), oidc.IDTokenVerifier (верифицирует токены), middleware-функция, которая оборачивает http.Handler.

На практике

Для production-решения нужно учесть:

  • Кэширование JWKS - ключи подписи меняются редко, но запрос к провайдеру на каждый запрос недопустим. go-oidc кэширует ключи автоматически.
  • Graceful degradation - если провайдер недоступен, но ключи уже закэшированы, верификация продолжает работать.
  • Обработка ошибок - разные статусы: 401 для отсутствия/невалидного токена, 403 для недостаточных прав.
  • Конфигурация - issuer, client ID, audience должны быть в env или конфиг-файле.
  • Логирование - фиксировать sub, iss, но не сам токен.
  • Тестирование - использовать mock JWKS endpoint или тестовые ключи.

Для микросервисной архитектуры имеет смысл вынести верификацию в общий пакет или отдельный sidecar (например, oauth2-proxy), чтобы не дублировать логику.

Пример кода

GO
package auth
import (
"context"
"net/http"
"strings"
"github.com/coreos/go-oidc/v3/oidc"
)
type Middleware struct {
verifier *oidc.IDTokenVerifier
}
func NewMiddleware(issuer, clientID string) (*Middleware, error) {
provider, err := oidc.NewProvider(context.Background(), issuer)
if err != nil {
return nil, err
}
verifier := provider.Verifier(&oidc.Config{
ClientID: clientID,
})
return &Middleware{verifier: verifier}, nil
}
func (m *Middleware) RequireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "missing token", http.StatusUnauthorized)
return
}
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
http.Error(w, "invalid auth header", http.StatusUnauthorized)
return
}
idToken, err := m.verifier.Verify(r.Context(), parts[1])
if err != nil {
http.Error(w, "invalid token: "+err.Error(), http.StatusUnauthorized)
return
}
var claims struct {
Sub string `json:"sub"`
Email string `json:"email"`
}
if err := idToken.Claims(&claims); err != nil {
http.Error(w, "failed to parse claims", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "user", claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}

Как отвечать на собеседовании

Начни с разделения OAuth 2.0 и OpenID Connect: первое - про авторизацию доступа, второе - про аутентификацию пользователя. Затем опиши поток: клиент получает токен, backend проверяет подпись через JWKS. Упомяни, что в Go стандартный подход - go-oidc + x/oauth2. Подчеркни, что верификация stateless и не требует обращения к провайдеру на каждый запрос. Если спросят про refresh tokens - объясни, что backend обычно их не видит, они живут на клиенте. Для senior-уровня добавь рассуждения о кэшировании JWKS, обработке rotation ключей и разнице между access token и ID token. Если интервьюер спросит про альтернативы - упомяни introspection endpoint для случаев, когда нужен revocation.

Что проверяет интервьюер

  • Понимание разницы между OAuth 2.0 и OpenID Connect.
  • Знание структуры JWT и алгоритмов подписи (RS256, ES256).
  • Умение объяснить, почему JWKS endpoint важен и как работает кэширование ключей.
  • Понимание security-аспектов: audience validation, issuer validation, защита от token substitution.
  • Практический опыт с Go-экосистемой: middleware, context, http.Handler.
  • Умение рассуждать о trade-off: stateless vs stateful, где хранить сессии, как обрабатывать logout.

Типичные ошибки

  • Путать OAuth 2.0 и OpenID Connect, называя OAuth "аутентификацией".
  • Проверять только подпись, но не iss и aud - это открывает возможность подмены токена.
  • Использовать симметричное шифрование (HS256) с секретом, который известен клиенту.
  • Делать HTTP-запрос к провайдеру на каждый запрос для проверки токена - убивает производительность.
  • Не обрабатывать rotation ключей - при смене ключей провайдера все запросы падают.
  • Хранить токен в логах или передавать через query string.
  • Игнорировать exp - токен может быть просрочен, но подпись валидна.
  • Не использовать PKCE для public clients - это уязвимость для SPA и мобильных приложений.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью