sequenceDiagram participant ClientApp participant User participant AuthServer participant ResourceServer %% Регистрация нового пользователя Note over ClientApp: 1. Создание нового пользователя ClientApp->>AuthServer: POST /register (не часть OAuth) AuthServer-->>ClientApp: client_id, client_secret %% Авторизация (Authorization Code Flow) Note over ClientApp,User: 2. Авторизация пользователя ClientApp->>User: Redirect to AuthURL (config.AuthCodeURL()) User->>AuthServer: Авторизация/согласие AuthServer->>User: Redirect с code и state User->>ClientApp: Передача code ClientApp->>AuthServer: Exchange code на token (config.Exchange()) AuthServer-->>ClientApp: access_token, refresh_token %% Использование токена Note over ClientApp,ResourceServer: 3. Доступ к ресурсам ClientApp->>ResourceServer: Запрос с токеном (transport) ResourceServer-->>ClientApp: Данные пользователя %% Другие поддерживаемые события Note over ClientApp,AuthServer: 4. Другие сценарии %% Refresh Token Flow ClientApp->>AuthServer: Запрос нового токена (TokenSource.Token()) AuthServer-->>ClientApp: Новый access_token %% Client Credentials Flow ClientApp->>AuthServer: Запрос токена (clientcredentials.Config) AuthServer-->>ClientApp: Сервисный токен %% Device Flow ClientApp->>AuthServer: Запрос device code (DeviceAuth()) AuthServer-->>ClientApp: user_code, verification_uri User->>AuthServer: Ввод user_code ClientApp->>AuthServer: Polling для токена (DeviceAccessToken()) AuthServer-->>ClientApp: Устройственный токен
Ключевые элементы модели:
-
Типы пакета OAuth2:
Config
- основной объект конфигурацииEndpoint
- URLs сервера авторизацииToken
- содержит access/refresh токеныTokenSource
- механизм обновления токеновTransport
- HTTP-транспорт с авторизацией
-
Основные методы:
AuthCodeURL()
- генерация URL авторизацииExchange()
- обмен code на токенClient()
- создание авторизованного HTTP-клиентаTokenSource()
- автоматическое обновление токенов
-
Поддерживаемые потоки:
- Authorization Code Flow (основной)
- Client Credentials Flow (для сервисов)
- Device Flow (для ТВ/IoT)
- Refresh Token Flow
-
Дополнительные компоненты:
- PKCE (защита от CSRF)
- Различные AuthStyle (методы аутентификации)
- Кастомные AuthCodeOption
Эта модель охватывает полный жизненный цикл OAuth 2.0 в Go-приложениях, от регистрации клиента до доступа к защищенным ресурсам.
Переменные
var HTTPClient internal.ContextKey
HTTPClient - это ключ контекста, используемый с context.WithValue для ассоциации *http.Client с контекстом.
var NoContext = context.TODO()
NoContext
- контекст по умолчанию, который следует использовать, если не применяется собственный context.Context.
Устарело: Вместо этого используйте context.Background или context.TODO.
Функции
func GenerateVerifier
func GenerateVerifier() string
GenerateVerifier генерирует верификатор кода PKCE со случайными 32 октетами. Соответствует рекомендациям RFC 7636.
Новый верификатор должен генерироваться для каждой авторизации. Полученный верификатор следует передавать в Config.AuthCodeURL или Config.DeviceAuth с S256ChallengeOption, а в Config.Exchange или Config.DeviceAccessToken - с VerifierOption.
func NewClient
func NewClient(ctx context.Context, src TokenSource) *http.Client
NewClient создает *http.Client из context.Context и TokenSource. Возвращенный клиент действителен только в течение времени жизни контекста.
Примечание: если пользовательский *http.Client предоставлен через context.Context, он используется только для получения токена и не влияет на *http.Client, возвращаемый из NewClient.
Особый случай: если src равен nil, возвращается не-OAuth2 клиент с использованием предоставленного контекста.
func RegisterBrokenAuthHeaderProvider
Устарело.
func S256ChallengeFromVerifier
func S256ChallengeFromVerifier(verifier string) string
S256ChallengeFromVerifier возвращает challenge-код PKCE, полученный из верификатора методом S256.
Предпочтительнее использовать S256ChallengeOption, где это возможно.
Типы
type AuthCodeOption
type AuthCodeOption interface {
// содержит неэкспортируемые методы
}
AuthCodeOption передается в Config.AuthCodeURL.
Варианты:
var (
// AccessTypeOnline и AccessTypeOffline - параметры для Options.AuthCodeURL.
// Они изменяют поле "access_type" в URL, возвращаемом AuthCodeURL.
AccessTypeOnline AuthCodeOption = SetAuthURLParam("access_type", "online")
AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")
// ApprovalForce требует от пользователя подтверждения запроса разрешений
ApprovalForce AuthCodeOption = SetAuthURLParam("prompt", "consent")
)
Объяснение AuthCodeOption
AuthCodeOption
- это интерфейс, который позволяет настраивать параметры Authorization Code Flow в OAuth 2.0. Он используется для:
- Модификации URL авторизации
- Передачи дополнительных параметров на endpoint авторизации
- Контроля поведения OAuth-диалога
Основные варианты использования
1. Управление типом доступа
// Запрос online-токена (по умолчанию)
url := conf.AuthCodeURL("state", oauth2.AccessTypeOnline)
// Запрос offline-токена с refresh token
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
Различие:
AccessTypeOnline
- токен действителен только во время текущей сессииAccessTypeOffline
- позволяет получить refresh token для долгоживущих доступов
2. Принудительное подтверждение разрешений
// Пользователь всегда увидит диалог подтверждения
url := conf.AuthCodeURL("state", oauth2.ApprovalForce)
Применение: Когда нужно гарантировать, что пользователь явно подтвердит запрашиваемые разрешения, даже если уже давал согласие ранее.
Дополнительные возможности
3. PKCE (защита от CSRF)
verifier := oauth2.GenerateVerifier()
url := conf.AuthCodeURL(
"state",
oauth2.AccessTypeOffline,
oauth2.S256ChallengeOption(verifier),
)
4. Кастомные параметры
// Добавление произвольных параметров
customParam := oauth2.SetAuthURLParam("custom_param", "value")
url := conf.AuthCodeURL("state", customParam)
Техническая реализация
Интерфейс реализуется через скрытые методы, а на практике используются:
SetAuthURLParam
- для установки произвольных параметров- Предопределенные варианты (AccessTypeOnline и др.)
- PKCE-реализации
Пример комплексного использования
func GetAuthURL(config *oauth2.Config) string {
verifier := oauth2.GenerateVerifier()
return config.AuthCodeURL(
"random-state-123",
oauth2.AccessTypeOffline, // Запрашиваем refresh token
oauth2.ApprovalForce, // Всегда показывать диалог подтверждения
oauth2.S256ChallengeOption(verifier), // Включение PKCE
oauth2.SetAuthURLParam("hd", "example.com"), // Ограничение домена
)
}
Ключевые преимущества:
- Гибкость настройки OAuth-потока
- Поддержка современных механизмов безопасности
- Единообразный API для всех параметров
- Возможность расширения функциональности
func S256ChallengeOption
func S256ChallengeOption(verifier string) AuthCodeOption
S256ChallengeOption создает PKCE challenge из верификатора методом S256. Должен передаваться только в Config.AuthCodeURL или Config.DeviceAuth.
func SetAuthURLParam
func SetAuthURLParam(key, value string) AuthCodeOption
SetAuthURLParam создает AuthCodeOption для передачи параметров key/value на endpoint авторизации провайдера.
func VerifierOption
func VerifierOption(verifier string) AuthCodeOption
VerifierOption возвращает AuthCodeOption с верификатором кода PKCE. Должен передаваться только в Config.Exchange или Config.DeviceAccessToken.
type AuthStyle
type AuthStyle int
AuthStyle определяет способ аутентификации запросов токенов.
Варианты:
const (
// Автоопределение стиля аутентификации
AuthStyleAutoDetect AuthStyle = 0
// Передача client_id и client_secret в теле POST
AuthStyleInParams AuthStyle = 1
// Использование HTTP Basic Authorization OAuth2 RFC 6749 section 2.3.1.
AuthStyleInHeader AuthStyle = 2
)
Объяснение AuthStyle
AuthStyle
определяет способ передачи учетных данных клиента (client_id и client_secret) при запросе токена в OAuth 2.0 flow. Это важный аспект безопасности, который влияет на:
- Способ передачи конфиденциальных данных
- Совместимость с различными OAuth-провайдерами
- Соответствие стандартам безопасности
Детальное описание вариантов
1. AuthStyleAutoDetect (значение 0)
AuthStyleAutoDetect AuthStyle = 0
Поведение:
- Автоматически определяет предпочтительный метод на основе:
- Ответа сервера
- Известных особенностей провайдера
- Пробует разные методы при необходимости
Пример использования:
endpoint := oauth2.Endpoint{
AuthURL: "https://provider.com/auth",
TokenURL: "https://provider.com/token",
AuthStyle: oauth2.AuthStyleAutoDetect,
}
Когда использовать: Когда провайдер поддерживает оба метода или его требования неизвестны.
2. AuthStyleInParams (значение 1)
AuthStyleInParams AuthStyle = 1
Поведение:
- Передает client_id и client_secret в теле POST-запроса
- Формат:
application/x-www-form-urlencoded
- Пример тела запроса:
client_id=CLIENT_ID&client_secret=CLIENT_SECRET&...
Пример использования:
endpoint := oauth2.Endpoint{
AuthURL: "https://provider.com/auth",
TokenURL: "https://provider.com/token",
AuthStyle: oauth2.AuthStyleInParams,
}
Когда использовать:
- Когда провайдер явно требует этот метод
- Для совместимости со старыми OAuth-реализациями
3. AuthStyleInHeader (значение 2)
AuthStyleInHeader AuthStyle = 2
Поведение:
- Использует HTTP Basic Authentication (RFC 6749 section 2.3.1)
- Данные передаются в заголовке Authorization:
Authorization: Basic BASE64(client_id:client_secret)
Пример использования:
endpoint := oauth2.Endpoint{
AuthURL: "https://provider.com/auth",
TokenURL: "https://provider.com/token",
AuthStyle: oauth2.AuthStyleInHeader,
}
Преимущества:
- Более безопасный метод (не попадает в логи)
- Рекомендуется стандартом OAuth 2.0
- Поддерживается большинством современных провайдеров
Практические рекомендации
-
Для публичных клиентов (SPA, мобильные приложения):
// Не используйте client_secret, только PKCE conf := &oauth2.Config{ ClientID: "public_client_id", Scopes: []string{"openid"}, Endpoint: oauth2.Endpoint{ AuthStyle: oauth2.AuthStyleInHeader, // Или AutoDetect }, }
-
Для конфиденциальных клиентов (серверные приложения):
conf := &oauth2.Config{ ClientID: "client_id", ClientSecret: "client_secret", Scopes: []string{"api:read"}, Endpoint: oauth2.Endpoint{ AuthStyle: oauth2.AuthStyleInHeader, // Предпочтительно }, }
-
При проблемах совместимости:
// Пробуем разные варианты styles := []oauth2.AuthStyle{ oauth2.AuthStyleInHeader, oauth2.AuthStyleInParams, } for _, style := range styles { endpoint.AuthStyle = style // Пробуем запрос токена }
Соответствие стандартам
AuthStyleInHeader
соответствует RFC 6749 section 2.3.1AuthStyleInParams
- устаревший, но широко поддерживаемый методAuthStyleAutoDetect
- удобная обертка для максимальной совместимости
Выбор правильного AuthStyle критически важен для безопасности и работоспособности OAuth-интеграции.
type Config
type Config struct {
// ClientID - публичный идентификатор вашего приложения,
// выдается провайдером при регистрации OAuth-клиента
ClientID string
// ClientSecret - секретный ключ вашего приложения,
// должен храниться безопасно (не в клиентском коде)
ClientSecret string
// Endpoint - содержит URL-адреса сервера авторизации:
// AuthURL - endpoint для получения authorization code
// TokenURL - endpoint для обмена code на access token
Endpoint Endpoint
// RedirectURL - URL, на который провайдер перенаправит
// пользователя после авторизации. Должен точно совпадать
// с URL, зарегистрированным у провайдера
RedirectURL string
// Scopes - запрашиваемые области доступа (разрешения),
// определяют, к каким ресурсам будет доступ у приложения
Scopes []string
}
Config описывает стандартный 3-этапный OAuth2-поток с информацией о клиентском приложении и URL endpoint’ов сервера.
Объяснение Config
Структура Config
является центральной конфигурацией для OAuth 2.0 Authorization Code Flow (3-legged OAuth). Она содержит все необходимые параметры для:
- Идентификации приложения (клиента)
- Настройки взаимодействия с OAuth-провайдером
- Управления процессом авторизации
Детальная расшифровка полей структуры
type Config struct {
// ClientID - публичный идентификатор вашего приложения,
// выдается провайдером при регистрации OAuth-клиента
ClientID string
// ClientSecret - секретный ключ вашего приложения,
// должен храниться безопасно (не в клиентском коде)
ClientSecret string
// Endpoint - содержит URL-адреса сервера авторизации:
// AuthURL - endpoint для получения authorization code
// TokenURL - endpoint для обмена code на access token
Endpoint Endpoint
// RedirectURL - URL, на который провайдер перенаправит
// пользователя после авторизации. Должен точно совпадать
// с URL, зарегистрированным у провайдера
RedirectURL string
// Scopes - запрашиваемые области доступа (разрешения),
// определяют, к каким ресурсам будет доступ у приложения
Scopes []string
}
Основные методы Config
1. AuthCodeURL - генерация URL для авторизации
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string
Пример использования:
// Базовый вариант
authURL := config.AuthCodeURL("random-state-123")
// С дополнительными параметрами
authURL := config.AuthCodeURL(
"state-xyz",
oauth2.AccessTypeOffline, // запросить refresh token
oauth2.ApprovalForce, // всегда показывать диалог подтверждения
oauth2.SetAuthURLParam("hd", "example.com"), // ограничение домена
)
2. Exchange - обмен authorization code на токен
func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error)
Пример использования:
// Обработка callback'а с code
func handleCallback(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
token, err := config.Exchange(r.Context(), code)
if err != nil {
// обработка ошибки
}
// использование токена...
}
3. Client - создание HTTP-клиента с авторизацией
func (c *Config) Client(ctx context.Context, t *Token) *http.Client
Пример использования:
// Создание клиента с автоматическим обновлением токенов
client := config.Client(context.Background(), token)
// Использование клиента для API-запросов
resp, err := client.Get("https://api.example.com/userinfo")
if err != nil {
// обработка ошибки
}
4. TokenSource - создание источника токенов
func (c *Config) TokenSource(ctx context.Context, t *Token) oauth2.TokenSource
Пример использования:
// Создание источника токенов с автоматическим обновлением
tokenSource := config.TokenSource(context.Background(), initialToken)
// Получение свежего токена
newToken, err := tokenSource.Token()
if err != nil {
// обработка ошибки
}
Полный пример использования
package main
import (
"context"
"fmt"
"log"
"net/http"
"golang.org/x/oauth2"
)
func main() {
config := &oauth2.Config{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Scopes: []string{"user:read", "files:write"},
RedirectURL: "https://yourapp.com/callback",
Endpoint: oauth2.Endpoint{
AuthURL: "https://auth.example.com/authorize",
TokenURL: "https://auth.example.com/token",
},
}
// Шаг 1: Перенаправление пользователя на авторизацию
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Перейдите по ссылке для авторизации:\n%v\n", authURL)
// Шаг 2: Обработка callback'а (эмулируем получение code)
var code string
fmt.Print("Введите полученный code: ")
if _, err := fmt.Scan(&code); err != nil {
log.Fatal(err)
}
// Шаг 3: Обмен code на токен
token, err := config.Exchange(context.Background(), code)
if err != nil {
log.Fatal(err)
}
// Шаг 4: Использование токена
client := config.Client(context.Background(), token)
resp, err := client.Get("https://api.example.com/user")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// Обработка ответа...
fmt.Println("Успешный запрос к API!")
}
Особенности и рекомендации
-
Безопасность:
- Никогда не храните ClientSecret в клиентском коде
- Всегда используйте HTTPS для RedirectURL
- Реализуйте проверку параметра state для защиты от CSRF
-
Жизненный цикл токенов:
- AccessToken обычно имеет ограниченное время жизни
- RefreshToken (если есть) позволяет получать новые AccessToken
- TokenSource автоматически обрабатывает обновление токенов
-
Для production-использования:
- Добавьте обработку ошибок на всех этапах
- Реализуйте хранение токенов между сессиями
- Добавьте PKCE для улучшенной безопасности
Тип Config предоставляет полный набор инструментов для реализации OAuth 2.0 Authorization Code Flow в Go-приложениях, охватывая все этапы процесса авторизации.
Пример
package main
import (
"context"
"fmt"
"log"
"golang.org/x/oauth2"
)
func main() {
ctx := context.Background()
conf := &oauth2.Config{
ClientID: "ВАШ_CLIENT_ID",
ClientSecret: "ВАШ_CLIENT_SECRET",
Scopes: []string{"ДОСТУП1", "ДОСТУП2"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://provider.com/o/oauth2/auth", // URL авторизации
TokenURL: "https://provider.com/o/oauth2/token", // URL получения токена
},
}
// Используем PKCE для защиты от CSRF атак
// Спецификация: https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-22.html#name-countermeasures-6
verifier := oauth2.GenerateVerifier() // Генерируем верификатор для PKCE
// Перенаправляем пользователя на страницу согласия для запроса разрешений
// на указанные выше области доступа (scopes)
url := conf.AuthCodeURL(
"state", // Уникальный state для защиты
oauth2.AccessTypeOffline, // Запрашиваем refresh-токен
oauth2.S256ChallengeOption(verifier), // Добавляем PKCE challenge
)
fmt.Printf("Перейдите по URL для авторизации: %v", url)
// Получаем authorization code из redirect URL.
// Exchange выполнит обмен кода на токен доступа.
// HTTP клиент от conf.Client будет автоматически обновлять токен.
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatal(err)
}
// Обмениваем код на токен, передавая верификатор PKCE
tok, err := conf.Exchange(ctx, code, oauth2.VerifierOption(verifier))
if err != nil {
log.Fatal(err)
}
// Создаем HTTP клиент с автоматическим обновлением токенов
client := conf.Client(ctx, tok)
client.Get("...") // Делаем авторизованные запросы
}
Пример CustomHTTP
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
"golang.org/x/oauth2"
)
func main() {
ctx := context.Background()
// Конфигурация OAuth2 клиента
conf := &oauth2.Config{
ClientID: "ВАШ_CLIENT_ID", // Идентификатор клиента
ClientSecret: "ВАШ_CLIENT_SECRET", // Секретный ключ клиента
Scopes: []string{"ДОСТУП1", "ДОСТУП2"}, // Запрашиваемые разрешения
Endpoint: oauth2.Endpoint{
TokenURL: "https://provider.com/o/oauth2/token", // URL для получения токена
AuthURL: "https://provider.com/o/oauth2/auth", // URL для авторизации
},
}
// Перенаправляем пользователя на страницу авторизации для получения разрешений
// на указанные области доступа (scopes)
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline) // "state" для защиты от CSRF
fmt.Printf("Перейдите по ссылке для авторизации: %v", url)
// Получаем код авторизации из redirect URL.
// Exchange выполняет обмен кода на токен доступа.
// HTTP клиент, возвращаемый conf.Client, будет автоматически обновлять токен.
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatal(err)
}
// Используем кастомный HTTP клиент с таймаутом 2 секунды для запроса токена
httpClient := &http.Client{Timeout: 2 * time.Second}
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
// Получаем токен доступа
tok, err := conf.Exchange(ctx, code)
if err != nil {
log.Fatal(err)
}
// Создаем HTTP клиент с автоматической авторизацией
client := conf.Client(ctx, tok)
_ = client // Используйте client для авторизованных запросов
}
func (*Config) AuthCodeURL
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string
AuthCodeURL
возвращает URL страницы согласия OAuth 2.0 провайдера, которая явно запрашивает разрешения для требуемых областей доступа (scopes).
Параметры:
-
state
Случайное значение (опaque), используемое клиентом для поддержания состояния между запросом и callback-вызовом. Сервер авторизации включает это значение при перенаправлении пользователя обратно в клиентское приложение. -
opts
(дополнительные опции)
Может включать:AccessTypeOnline
илиAccessTypeOffline
- тип доступаApprovalForce
- принудительное подтверждение разрешений
Защита от CSRF-атак:
-
Рекомендуемый способ:
Включите PKCE challenge черезS256ChallengeOption
.
Примечание: Не все серверы поддерживают PKCE. -
Альтернативный способ:
Генерация случайного параметраstate
с последующей проверкой после обмена токена.
Ссылки на стандарты:
- RFC 6749, раздел 10.12 (предшествовал PKCE)
- PKCE в OAuth
- Защита от CSRF в OAuth 2.1
func (*Config) Client
func (c *Config) Client(ctx context.Context, t *Token) *http.Client
Клиент возвращает HTTP-клиента, используя предоставленный токен. Токен будет автоматически обновляться по мере необходимости. Базовый HTTP-транспорт будет получен с использованием предоставленного контекста. Возвращенный клиент и его Transport не должны быть изменены.
func (*Config) DeviceAccessToken
func (c *Config) DeviceAccessToken(ctx context.Context, da *DeviceAuthResponse, opts ...AuthCodeOption) (*Token, error)
DeviceAccessToken опрашивает сервер для обмена device code на токен.
func (*Config) DeviceAuth
func (c *Config) DeviceAuth(ctx context.Context, opts ...AuthCodeOption) (*DeviceAuthResponse, error)
DeviceAuth возвращает структуру с device code для авторизации на другом устройстве.
Пример
var config Config
ctx := context.Background()
response, err := config.DeviceAuth(ctx)
if err != nil {
panic(err)
}
fmt.Printf("please enter code %s at %s\n", response.UserCode, response.VerificationURI)
token, err := config.DeviceAccessToken(ctx, response)
if err != nil {
panic(err)
}
fmt.Println(token)
func (*Config) Exchange
func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error)
Выполняет обмен authorization code на токен доступа.
Использование: Вызывается после перенаправления пользователя обратно на Redirect URI (URL, полученный из AuthCodeURL).
Параметры:
ctx
- контекст, может содержать кастомный HTTP-клиент (см. переменную HTTPClient)code
- authorization code из параметраhttp.Request.FormValue("code")
opts
- опции, при использовании PKCE должен включать VerifierOption
Безопасность:
- Обязательно проверяйте параметр
state
(изhttp.Request.FormValue("state")
) перед вызовом для защиты от CSRF - Для PKCE передавайте верификатор через VerifierOption
func (*Config) PasswordCredentialsToken
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error)
Получает токен по связке username/password (Resource Owner Password Credentials flow).
Рекомендации RFC 6749: Следует использовать ТОЛЬКО при:
- Высокой степени доверия между клиентом и владельцем ресурса
- Когда клиент является частью ОС или привилегированного приложения
- При отсутствии других доступных способов авторизации
Ссылка: RFC 6749 Section 4.3
Параметры:
ctx
- может содержать кастомный HTTP-клиентusername
,password
- учетные данные владельца ресурса
Примечание: Этот grant type считается менее безопасным и должен применяться в исключительных случаях.
func (*Config) TokenSource
func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource
TokenSource возвращает источник токенов с автоматическим обновлением.
type DeviceAuthResponse
type DeviceAuthResponse struct {
// DeviceCode - код устройства для OAuth-потока
DeviceCode string `json:"device_code"`
// UserCode - код, который пользователь должен ввести
// на странице верификации
UserCode string `json:"user_code"`
// VerificationURI - URL страницы, куда пользователь
// должен ввести user code
VerificationURI string `json:"verification_uri"`
// VerificationURIComplete (опционально) - полный URL верификации
// с уже подставленным user code. Обычно отображается пользователю
// в графической форме (например, QR-код)
VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
// Expiry - время истечения срока действия
// device code и user code
Expiry time.Time `json:"expires_in,omitempty"`
// Interval - интервал в секундах между запросами
// на проверку авторизации (polling)
Interval int64 `json:"interval,omitempty"`
}
DeviceAuthResponse описывает успешный ответ Device Authorization по RFC 8628.
Объяснение DeviceAuthResponse
DeviceAuthResponse
представляет ответ сервера авторизации в OAuth 2.0 Device Authorization Grant Flow (RFC 8628). Этот тип используется для реализации “потока устройства” - специального сценария авторизации для устройств с ограниченными возможностями ввода (Smart TV, IoT устройства, принтеры и т.д.).
Основные функции:
- Получение кодов верификации для пользователя
- Предоставление инструкций для завершения авторизации
- Управление процессом polling для получения токена
Пример полного цикла использования
1. Инициализация потока устройства
func InitDeviceFlow(config *oauth2.Config) (*oauth2.DeviceAuthResponse, error) {
ctx := context.Background()
// Запрос параметров device flow
deviceAuth, err := config.DeviceAuth(ctx)
if err != nil {
return nil, fmt.Errorf("ошибка инициализации device flow: %v", err)
}
return deviceAuth, nil
}
2. Отображение инструкций пользователю
func ShowUserInstructions(deviceAuth *oauth2.DeviceAuthResponse) {
fmt.Printf("1. Перейдите на страницу: %s\n", deviceAuth.VerificationURI)
fmt.Printf("2. Введите код: %s\n", deviceAuth.UserCode)
// Если доступен QR-код
if deviceAuth.VerificationURIComplete != "" {
fmt.Printf("Или отсканируйте QR-код: %s\n", deviceAuth.VerificationURIComplete)
}
fmt.Printf("Код действителен до: %v\n", deviceAuth.Expiry)
}
3. Получение токена (polling)
func PollForToken(config *oauth2.Config, deviceAuth *oauth2.DeviceAuthResponse) (*oauth2.Token, error) {
ctx := context.Background()
for {
// Запрос токена с использованием device code
token, err := config.DeviceAccessToken(ctx, deviceAuth)
if err == nil {
return token, nil
}
// Обработка ошибок
if oauth2Err, ok := err.(*oauth2.RetrieveError); ok {
if oauth2Err.ErrorCode == "authorization_pending" {
// Ожидание следующего polling-запроса
time.Sleep(time.Duration(deviceAuth.Interval) * time.Second)
continue
}
}
return nil, fmt.Errorf("ошибка получения токена: %v", err)
}
}
Полный пример интеграции
package main
import (
"context"
"fmt"
"log"
"time"
"golang.org/x/oauth2"
)
func main() {
config := &oauth2.Config{
ClientID: "your-client-id",
Scopes: []string{"user.read"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
TokenURL: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
DeviceURL: "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode",
},
}
// 1. Инициализация device flow
deviceAuth, err := config.DeviceAuth(context.Background())
if err != nil {
log.Fatalf("Ошибка инициализации: %v", err)
}
// 2. Показ инструкций пользователю
fmt.Println("\n=== Инструкции по авторизации ===")
fmt.Printf("Пожалуйста, откройте %s\n", deviceAuth.VerificationURI)
fmt.Printf("И введите код: %s\n\n", deviceAuth.UserCode)
// 3. Polling для получения токена
fmt.Println("Ожидание авторизации...")
token, err := PollForToken(config, deviceAuth)
if err != nil {
log.Fatalf("Ошибка получения токена: %v", err)
}
fmt.Printf("\nУспешная авторизация! Токен: %v\n", token.AccessToken)
}
Особенности работы с DeviceAuthResponse
-
Таймауты и интервалы:
- Учитывайте
Expiry
- код устройства имеет ограниченное время жизни - Соблюдайте
Interval
между polling-запросами
- Учитывайте
-
Пользовательский опыт:
- Для лучшего UX используйте
VerificationURIComplete
(QR-код) - Предусмотрите возможность отмены ожидания
- Для лучшего UX используйте
-
Безопасность:
- Не кэшируйте device code без необходимости
- Обрабатывайте все возможные ошибки сервера
-
Поддерживаемые провайдеры:
- Microsoft Identity Platform
- Google OAuth 2.0
- Другие совместимые с RFC 8628
Этот тип особенно полезен для:
- Устройств без браузера
- Приложений с ограниченным вводом
- Сценариев, где нельзя использовать стандартный OAuth flow
func (DeviceAuthResponse) MarshalJSON
func (d DeviceAuthResponse) MarshalJSON() ([]byte, error)
func (*DeviceAuthResponse) UnmarshalJSON
func (c *DeviceAuthResponse) UnmarshalJSON(data []byte) error
type Endpoint
type Endpoint struct {
AuthURL string // URL endpoint'а авторизации (для получения authorization code)
DeviceAuthURL string // URL для Device Flow авторизации (RFC 8628)
TokenURL string // URL endpoint'а получения токенов (для обмена code на token)
AuthStyle AuthStyle // Предпочтительный метод аутентификации клиента
}
Предназначение: Содержит все необходимые URL для OAuth 2.0 flow.
Использование: Конфигурация клиента для взаимодействия с OAuth провайдером.
type RetrieveError
type RetrieveError struct {
Response *http.Response // HTTP-ответ сервера
Body []byte // Тело ответа (может быть усечено)
ErrorCode string // Код ошибки OAuth (RFC 6749 Section 5.2)
ErrorDescription string // Человекочитаемое описание ошибки
ErrorURI string // Ссылка на документацию по ошибке
}
RetrieveError - ошибка, возвращаемая при неверном статусе HTTP или ошибке OAuth2.
Предназначение: Ошибки, возвращаемые OAuth сервером.
Особенности:
- Соответствует стандарту OAuth 2.0 (RFC 6749)
- Включает как HTTP-детали, так и специфичные OAuth ошибки
type Token
type Token struct {
AccessToken string // Основной токен доступа
TokenType string // Тип токена (обычно "Bearer")
RefreshToken string // Токен для обновления (опционально)
Expiry time.Time // Время истечения срока действия
ExpiresIn int64 // Срок жизни токена в секундах
}
Token содержит учетные данные для авторизации запросов.
Предназначение: Хранит OAuth 2.0 токены и метаданные.
- AccessToken всегда обязателен
- RefreshToken может отсутствовать в некоторых flow
- Expiry вычисляется из ExpiresIn при получении токена
type TokenSource
type TokenSource interface {
Token() (*Token, error) // Возвращает текущий/новый токен
}
TokenSource - любой источник, который может возвращать токен.
Предназначение: Абстракция для источников токенов.
Реализации:
- StaticTokenSource (фиксированный токен)
- ReuseTokenSource (с автоматическим обновлением)
- Config.TokenSource (получение токенов из Config)
type Transport
type Transport struct {
Source TokenSource // Источник токенов для авторизации
Base http.RoundTripper // Базовый RoundTripper (по умолчанию http.DefaultTransport)
}
Transport - это http.RoundTripper для OAuth 2.0 запросов.
Предназначение: Добавляет OAuth-авторизацию к HTTP-запросам.
Принцип работы:
- Получает токен из Source
- Добавляет Authorization header
- Делегирует запрос базовому RoundTripper