Documentation
¶
Overview ¶
Package oauthdevice serves the device authorization grant (RFC 8628) of plugin/oauthprovider with plugin/deviceauth, better-auth's oauthDeviceAuthorization: a registered OAuth client (a CLI, a TV app) asks for a device code, the user approves on the approval page, and the client polls the provider's token endpoint with grant_type urn:ietf:params:oauth:grant-type:device_code for access, refresh and ID tokens instead of a session.
dev := oauthdevice.New("https://app.example.com/auth/device/code")
dp := deviceauth.New(deviceauth.WithGrant(dev))
op := oauthprovider.New(jw, oauthprovider.WithGrant(dev.ExtensionGrant()),
oauthprovider.DeviceAuthorizationEndpoint(dev.Endpoint()))
auth.WithPlugins(..., dp, op, dev)
At /device/code a client the provider knows is authenticated as at its token endpoint (client_secret_basic, client_secret_post, private_key_jwt, or none for a public client), must have registered the device grant, and may ask only for scopes and a resource it is allowed. Its codes are bound to the grant: they are redeemed only at the provider's token endpoint, never for a session at /device/token. A client id the provider does not know falls through to deviceauth's standalone flow and its ValidateClient, as better-auth's does.
Index ¶
- Constants
- Variables
- type Integration
- func (g *Integration) AuthorizeCode(ctx context.Context, c *auth.Call, in deviceauth.CodeRequest) (deviceauth.Binding, error)
- func (g *Integration) Endpoint() string
- func (g *Integration) ExtensionGrant() oauthprovider.ExtensionGrant
- func (g *Integration) Init(a *auth.Auth) error
- func (g *Integration) Name() string
- func (g *Integration) Requires() []string
- func (g *Integration) Validate(*auth.Config) error
Constants ¶
const GrantName = "oauth"
GrantName is the name codes bound by this grant carry.
const PluginName = "oauthdevice"
PluginName is the plugin's permanent identity.
Variables ¶
var ( ErrAuthorizationPending = auth.NewError(errorNamespace+".authorization_pending", http.StatusBadRequest, "The user has not approved the request yet.") ErrSlowDown = auth.NewError(errorNamespace+".slow_down", http.StatusBadRequest, "Polling too often. Wait longer between requests.") ErrExpiredToken = auth.NewError(errorNamespace+".expired_token", http.StatusBadRequest, "The device code has expired.") ErrAccessDenied = auth.NewError(errorNamespace+".access_denied", http.StatusBadRequest, "The user denied the request.") )
Wire errors of the token endpoint, under errorNamespace: the provider answers "authorization_pending", "slow_down", "expired_token" and "access_denied" for them (RFC 8628 §3.5).
var ErrNoEndpoint = errors.New("oauthdevice: New needs the absolute URL of /device/code")
ErrNoEndpoint is a configuration error: New needs the absolute URL of the device authorization endpoint (the assertion audience and the discovery value).
Functions ¶
This section is empty.
Types ¶
type Integration ¶
type Integration struct {
// contains filtered or unexported fields
}
Integration joins the two plugins. Build it with New, pass it to deviceauth.WithGrant and its ExtensionGrant to oauthprovider.WithGrant, and register it beside both.
func New ¶
func New(endpoint string) *Integration
New builds the integration for the device authorization endpoint at endpoint.
func (*Integration) AuthorizeCode ¶
func (g *Integration) AuthorizeCode(ctx context.Context, c *auth.Call, in deviceauth.CodeRequest) (deviceauth.Binding, error)
AuthorizeCode authorizes a /device/code request for a client the provider knows, and leaves any other to the standalone flow.
func (*Integration) Endpoint ¶
func (g *Integration) Endpoint() string
Endpoint is the device authorization endpoint's URL, for oauthprovider.DeviceAuthorizationEndpoint.
func (*Integration) ExtensionGrant ¶
func (g *Integration) ExtensionGrant() oauthprovider.ExtensionGrant
ExtensionGrant is the device_code grant for oauthprovider.WithGrant.
func (*Integration) Init ¶
func (g *Integration) Init(a *auth.Auth) error
Init finds both plugins. Requires has the core refuse a missing one before Init runs, so neither lookup fails in practice; both errors are still returned rather than assumed away.
func (*Integration) Requires ¶
func (g *Integration) Requires() []string
Requires names both plugins.