package models import ( "crypto/rand" "crypto/sha256" "crypto/subtle" "encoding/base64" "fmt" ) const ( DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code" ) type AuthorizationRequest struct { Challenge string `url:"code_challenge" form:"code_challenge" json:"code_challenge"` // RFC7636 ChallengeMethod PKCEChallengeType `url:"code_challenge_method" form:"code_challenge_method" json:"code_challenge_method"` // RFC7636 ClientId string `url:"client_id" form:"client_id" json:"client_id"` Scope string `url:"scope" form:"scope" json:"scope"` } type DeviceAuthorizationResponse struct { DeviceCode string `json:"device_code"` // REQUIRED UserCode string `json:"user_code"` // REQUIRED VerificationUri string `json:"verification_uri"` // REQUIRED VerificationUriComplete string `json:"verification_uri_complete,omitempty"` ExpiresIn int `json:"expires_in,omitempty"` Interval int `json:"interval,omitempty"` } type DeviceAccessTokenRequest struct { GrantType string `url:"grant_type" form:"grant_type" json:"grant_type"` DeviceCode string `url:"device_code" form:"device_code" json:"device_code"` ClientId string `url:"client_id" form:"client_id" json:"client_id"` CodeVerifier string `url:"code_verifier" form:"code_verifier" json:"code_verifier"` } type AccessTokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` // Must be Bearer ExpiresIn string `json:"expires_in,omitempty"` // Lifetime in seconds RefreshToken string `json:"refresh_token,omitempty"` Scope string `json:"scope,omitempty"` } type AuthorizationError string const ( ErrInvalidRequest AuthorizationError = "invalid_request" ErrInvalidClient = "invalid_client" ErrInvalidGrant = "invalid_grant" ErrUnauthorizedClient = "unauthorized_client" ErrUnsupportedGrantType = "unsupported_grant_type" ErrInvalidScope = "invalid_scope" ErrAuthorizationPending = "authorization_pending" // RFC7636 ErrSlowDown = "slow_down" // RFC7636 ErrAccessDenied = "access_denied" // RFC7636 ErrExpiredToken = "expired_token" // RFC7636 ) type Oauth2Error struct { Type AuthorizationError `json:"error"` Description string `json:"error_description,omitempty"` Uri string `json:"error_uri,omitempty"` } func (e Oauth2Error) Error() string { if e.Description == "" { return fmt.Sprintf("Oauth2Error: %s", e.Type) } else { return fmt.Sprintf("Oauth2Error: %s %s", e.Type, e.Description) } } type PKCEChallengeType string const ( ChallengePlain PKCEChallengeType = "plain" ChallengeS256 = "S256" ) type PKCEChallenge struct { Verifier string } func NewPKCEChallenge() (*PKCEChallenge, error) { buf := make([]byte, 32) if _, err := rand.Read(buf); err != nil { return nil, err } return &PKCEChallenge{ Verifier: base64.URLEncoding.EncodeToString(buf), }, nil } func (c *PKCEChallenge) Challenge() string { hash := sha256.Sum256([]byte(c.Verifier)) return base64.URLEncoding.EncodeToString(hash[:]) } func (c *PKCEChallenge) EqualString(o string) bool { return subtle.ConstantTimeCompare([]byte(o), []byte(c.Challenge())) != 1 }