Пакет OAuth2 языка программирования Go

Пакет oauth2 содержит реализацию клиента для спецификации OAuth 2.0.
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: Устройственный токен

Ключевые элементы модели:

  1. Типы пакета OAuth2:

    • Config - основной объект конфигурации
    • Endpoint - URLs сервера авторизации
    • Token - содержит access/refresh токены
    • TokenSource - механизм обновления токенов
    • Transport - HTTP-транспорт с авторизацией
  2. Основные методы:

    • AuthCodeURL() - генерация URL авторизации
    • Exchange() - обмен code на токен
    • Client() - создание авторизованного HTTP-клиента
    • TokenSource() - автоматическое обновление токенов
  3. Поддерживаемые потоки:

    • Authorization Code Flow (основной)
    • Client Credentials Flow (для сервисов)
    • Device Flow (для ТВ/IoT)
    • Refresh Token Flow
  4. Дополнительные компоненты:

    • 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")
)

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
)

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’ов сервера.

Пример
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).

Параметры:

  1. state
    Случайное значение (опaque), используемое клиентом для поддержания состояния между запросом и callback-вызовом. Сервер авторизации включает это значение при перенаправлении пользователя обратно в клиентское приложение.

  2. opts (дополнительные опции)
    Может включать:

    • AccessTypeOnline или AccessTypeOffline - тип доступа
    • ApprovalForce - принудительное подтверждение разрешений

Защита от CSRF-атак:

  1. Рекомендуемый способ:
    Включите PKCE challenge через S256ChallengeOption.
    Примечание: Не все серверы поддерживают PKCE.

  2. Альтернативный способ:
    Генерация случайного параметра state с последующей проверкой после обмена токена.

Ссылки на стандарты:

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

Безопасность:

  1. Обязательно проверяйте параметр state (из http.Request.FormValue("state")) перед вызовом для защиты от CSRF
  2. Для 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.

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