goauth

package module
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 21, 2022 License: MIT Imports: 17 Imported by: 0

README

goauth2

The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf.

From https://www.rfc-editor.org/rfc/rfc6749


Introduction

goauth2 is a Go library to create OAuth2 servers. It is made with ease-of-use in mind, allowing fast prototyping by offering useful defaults, ready-to-use in-memory storage implementations and basic HTML templates required for redirect based authorization flows.

Nearly every aspect of this OAuth2 implementation is modifiable.

Disclaimer: this is work in progress. That means there will still be breaking changes!

Terminology

If you are already an experienced OAuth2 user, you can skip this paragraph.

  • Resource Owner: the user who owns a resource.
  • Client: the app that wants to access a resource.
    • Confidential Client: a client that can keep a secret, e.g. server-side web apps
    • Public Client: a client that cannot keep a secret, e.g. mobile or desktop apps
  • User-Agent: some kind of client able to execute HTTP requests, but the actual client must not be able to access its storage. Typically a web browser.
  • Authorization Server: the heart of all authorization and authentication flows.
  • Resource Server: the server that actually has resource and is willing to supply it on the condition of correct authorization.
  • PKCE: (pronounced 'pixie'): stands for Proof Key for Code Exchange and is an extension of top of OAuth2 used for public clients using Authorization Code Grant mitigating authorization code interception attacks.
  • OIDC: stands for OpenID Connect, an additional identity layer on top of OAuth2 which allows clients to verify the identity of resource owners and to obtain basic profile information about those resource owners.

Grant Types

So far, goauth2 supports the following grant types:

  • Client Credentials Grant
  • Resource Owner Password Credentials Grant
  • Implicit Grant
  • Device Code Grant
  • Authorization Code Grant
  • Authorization Code Grant with Proof Key for Code Exchange
  • OpenID Connect

Explanations of Grant Types

Client Credentials Grant

The client can request an access token using only its client credentials when the client is requesting access to the protected resources under its control, or those of another resource owner that have been previously arranged with the authorization server. A typical use-case is machine-to-machine communication.

The client credentials grant MUST only be used by confidential clients.

+---------+                                  +---------------+
|         |                                  |               |
|         |>--(A)-- Client Authentication -->| Authorization |
| Client  |                                  |     Server    |
|         |<--(B)---- Access Token ---------<|               |
|         |                                  |               |
+---------+                                  +---------------+
Resource Owner Password Credentials Grant

The resource owner password credentials grant type is suitable in cases where the resource owner has a trust relationship with the client, such as the device operating system or a highly privileged application. The authorization server should take special care when enabling this grant type and only allow it when other flows are not viable.

This grant type is suitable for clients capable of obtaining the resource owner’s credentials (username and password, typically using an interactive form). It is also used to migrate existing clients using direct authentication schemes such as HTTP Basic or Digest authentication to OAuth by converting the stored credentials to an access token.

+----------+
| Resource |
|  Owner   |
|          |
+----------+
     v
     |  Resource Owner
    (A) Password Credentials
     |
     v
+---------+                                  +---------------+
|         |>--(B)---- Resource Owner ------->|               |
|         |         Password Credentials     | Authorization |
| Client  |                                  |     Server    |
|         |<--(C)---- Access Token ---------<|               |
|         |    (w/ Optional Refresh Token)   |               |
+---------+                                  +---------------+
Implicit Grant

The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI. These clients are typically implemented in a browser using a scripting language such as JavaScript.

Unlike the authorization code grant type, in which the client makes separate requests for authorization and for an access token, the client receives the access token as the result of the authorization request.

The implicit grant type does not include client authentication, and relies on the presence of the resource owner and the registration of the redirection URI. Because the access token is encoded into the redirection URI, it may be exposed to the resource owner and other applications residing on the same device.

+----------+
| Resource |
|  Owner   |
|          |
+----------+
     ^
     |
    (B)
+----|-----+          Client Identifier     +---------------+
|         -+----(A)-- & Redirection URI --->|               |
|  User-   |                                | Authorization |
|  Agent  -|----(B)-- User authenticates -->|     Server    |
|          |                                |               |
|          |<---(C)--- Redirection URI ----<|               |
|          |          with Access Token     +---------------+
|          |            in Fragment
|          |                                +---------------+
|          |----(D)--- Redirection URI ---->|   Web-Hosted  |
|          |          without Fragment      |     Client    |
|          |                                |    Resource   |
|     (F)  |<---(E)------- Script ---------<|               |
|          |                                +---------------+
+-|--------+
  |    |
 (A)  (G) Access Token
  |    |
  ^    v
+---------+
|         |
|  Client |
|         |
+---------+
Device Code Grant

It is also called Device Authorization Grant and Device Flow.

The Device Code grant type is used by browser-less or input-constrained devices in the device flow to exchange a previously obtained device code for an access token. It can also be used for applications that cannot handle redirects well, like desktop applications.

+----------+                                +----------------+
|          |>---(A)-- Client Identifier --->|                |
|          |                                |                |
|          |<---(B)-- Device Code,      ---<|                |
|          |          User Code,            |                |
|  Device  |          & Verification URI    |                |
|  Client  |                                |                |
|          |  [polling]                     |                |
|          |>---(E)-- Device Code       --->|                |
|          |          & Client Identifier   |                |
|          |                                |  Authorization |
|          |<---(F)-- Access Token      ---<|     Server     |
+----------+   (& Optional Refresh Token)   |                |
      v                                     |                |
      |                                     |                |
     (C) User Code & Verification URI       |                |
      |                                     |                |
      v                                     |                |
+----------+                                |                |
| End User |                                |                |
|    at    |<---(D)-- End user reviews ---->|                |
|  Browser |          authorization request |                |
+----------+                                +----------------+
Authorization Code Grant

The authorization code is a temporary code that the client will exchange for an access token. The code itself is obtained from the authorization server where the user gets a chance to see what
information the client is requesting, and approve or deny the request.

The authorization code flow offers a few benefits over the other grant types. When the user authorizes the application, they are redirected back to the application with a temporary code in the URL. The application exchanges that code for the access token. When the application makes the request for the access token, that request can be authenticated with the client secret, which reduces the risk of an attacker intercepting the authorization code and using it themselves. This also means the access token is never visible to the user or their browser, so it is the most secure way to pass the token back to the application, reducing the risk of the token leaking to someone else.

+----------+
| Resource |
|   Owner  |
|          |
+----------+
     ^
     |
    (B)
+----|-----+          Client Identifier      +---------------+
|         -+----(A)-- & Redirection URI ---->|               |
|  User-   |                                 | Authorization |
|  Agent  -+----(B)-- User authenticates --->|     Server    |
|          |                                 |               |
|         -+----(C)-- Authorization Code ---<|               |
+-|----|---+                                 +---------------+
  |    |                                         ^      v
 (A)  (C)                                        |      |
  |    |                                         |      |
  ^    v                                         |      |
+---------+                                      |      |
|         |>---(D)-- Authorization Code ---------'      |
|  Client |          & Redirection URI                  |
|         |                                             |
|         |<---(E)----- Access Token -------------------'
+---------+       (w/ Optional Refresh Token)
Authorization Code Grant with Proof Key for Code Exchange

PKCE (described in RFC 7636) is typically used in conjunction with the Authorization Code Grant.

Before redirecting the user to the authorization server, the client first generates a secret code verifier and challenge.

The code verifier is a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long.

Once the client has generated the code verifier, it uses that to create the code challenge. For devices that can perform a SHA256 hash, the code challenge is a BASE64-URL-encoded string of the SHA256 hash of the code verifier. Otherwise, the same verifier string is used as the challenge.

When exchanging the code for the access token, the code verifier must be sent over as well. The server will calculate the BASE64-URL-encoded string of the SHA256 hash of the code verifier and check if the result matches the initial code challenge.

                                          +-------------------+
                                          |   Authz Server    |
+--------+                                | +---------------+ |
|        |--(A)- Authorization Request ---->|               | |
|        |       + t(code_verifier), t_m  | | Authorization | |
|        |                                | |    Endpoint   | |
|        |<-(B)---- Authorization Code -----|               | |
|        |                                | +---------------+ |
| Client |                                |                   |
|        |                                | +---------------+ |
|        |--(C)-- Access Token Request ---->|               | |
|        |          + code_verifier       | |    Token      | |
|        |                                | |   Endpoint    | |
|        |<-(D)------ Access Token ---------|               | |
+--------+                                | +---------------+ |
                                          +-------------------+
Refresh Token Grant

Technically, this is not an actual authorization grant, but a renewal process for previously performed authorization.

If valid and authorized, the authorization server MAY issue an access token as described in Section 5.1 of RFC 6749.

The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client. If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the initial request.

+--------+                      +--------------+
|        |                      |              |
| Client |--(A) Refresh Token ->| Authz Server |
|        |                      |              |
|        |<--(B) New token(s) --|              |
|        |                      |              |
+--------+                      +--------------+

Examples

Examples can be found in the examples folder and consist of a server and a client implementation. They should be quite self-explanatory considering the abundance of code comments and links to the RFCs. If there are still things unclear, please open an issue and I will try to address it.

The following examples are currently available:

  • Client Credentials Grant
  • Resource Owner Password Credentials Grant
  • Implicit Grant
  • Device Code Grant
  • Authorization Code Grant
  • Authorization Code Grant with Proof Key for Code Exchange

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrLoggedIn = errors.New("user is already logged in")
)

Functions

func ErrorJSONResponse

func ErrorJSONResponse(w http.ResponseWriter, code int, errType ErrorType, errMsg string) error

func ErrorRedirectResponse

func ErrorRedirectResponse(w http.ResponseWriter, r *http.Request, redirectUrl string, errType ErrorType, errMsg, state string)

Types

type ErrorType

type ErrorType string
const (
	InvalidRequest          ErrorType = "invalid_request"
	UnauthorizedClient      ErrorType = "unauthorized_client"
	AccessDenied            ErrorType = "access_denied"
	UnsupportedResponseType ErrorType = "unsupported_response_type"
	InvalidScope            ErrorType = "invalid_scope"
	ServerError             ErrorType = "server_error"
	TemporarilyUnavailable  ErrorType = "temporarily_unavailable"
)

type Flags

type Flags struct {
	// PKCE = Proof Key for Code Exchange. Used on top of the Authorization Code Grant.
	PKCE bool
	// OIDC = OpenID Connect. If you set this to true, PKCE is enabled regardless of value.
	OIDC bool
}

Flags contains feature flags for the Authorization Code Grant to enable/disable particular features.

type Policies

type Policies struct {
	// DeviceCodeLength sets the length in bytes a generated device code for the Device Code Grant should have.
	DeviceCodeLength int
	// UserCodeLength sets the length in bytes a generated user code for the Device Code Grant should have.
	//
	// Deprecated: use the new UserCodeGenerator field instead.
	UserCodeLength int
	// AccessTokenLength sets the length in bytes of a generated access token.
	AccessTokenLength int
	// RefreshTokenLength sets the length in bytes of a generated refresh token.
	RefreshTokenLength int
	// ClientSecretLength sets the length in bytes of a generated client secret for newly created client.
	ClientSecretLength int
	// IDTokenLength relates to OpenID Connect and sets the length in bytes of a generated ID token.
	IDTokenLength int

	// SessionLifetime sets the maximum lifetime of a user session.
	SessionLifetime time.Duration
	// SessionLifetime sets the maximum lifetime of an access token.
	AccessTokenLifetime time.Duration
	// RefreshTokenLifetime sets the maximum lifetime of a refresh token.
	RefreshTokenLifetime time.Duration
}

Policies represents constraints and requirements for proper operation.

type Server

type Server struct {
	// PublicBaseURL is the public facing URL containing scheme, hostname and port, if required.
	// it is used to construct redirect URLs.
	PublicBaseURL string
	// Storage contains the necessary storage implementations.
	Storage Storage
	// Template contains HTML templates as byte slices used for displaying to the user, e.g. login form.
	Template Templates
	// Flags are feature flags meant to enable certain features.
	Flags Flags
	// Policies can restrict how certain values have to be restricted, e.g. the length of certain strings or the
	// validitdy durations.
	Policies Policies
	// Session contains session and cookie configuration values.
	Session Session
	// URLs contain paths and URLs for internal redirects.
	URLs URLs
	// TokenGenerator is a source used to generate tokens.
	TokenGenerator token.TokenGenerator
	// UserCodeGenerator is a source used to generate user codes for the device flow
	UserCodeGenerator usercode.Generator

	ErrorRedirect func(http.ResponseWriter, *http.Request, string, ErrorType, string, string)
	ErrorResponse func(http.ResponseWriter, int, ErrorType, string) error
	// contains filtered or unexported fields
}

A Server handles all HTTP requests relevant to the OAuth2 authorization processes. If a Server's exported fields are modified after first use, the behavior is undefined.

func NewDefaultServer

func NewDefaultServer() *Server

NewDefaultServer returns a *Server with set default values:

  • PublicBaseURL: is set to 'http://localhost' without a port. It is required for redirect-based authorization flows.

  • Storage: each store uses a corresponding in-memory implementation, e.g. MemoryClientStorage.

  • Templates: the default templates from this library are used. They are not overly pretty, but they get their job done.

  • Flags: all flags remain at their default value.

  • Policies: sensible lengths and lifetime which ensure a certain degree of security.

  • TokenGenerator: uses a ready-to-use in-memory implementation, namely DefaultTokenGenerator.

  • DefaultUserCodeGenerator: uses a ready-to-use in-memory implementation, namely DefaultUserCodeGenerator.

  • grantTypes: all implemented grant types are listed here.

You should probably alter the PublicBaseURL and add at least one Client and one User.

func NewEmptyServer

func NewEmptyServer() *Server

NewEmptyServer returns a *Server with just the base setup.

func (*Server) AddGrantType

func (s *Server) AddGrantType(gt types.GrantType)

AddGrantType adds the given grant type to the current list of enabled grant types for the server s. A grant type not listed might not be available, depending on the caller's usage. You can use this call to change the availability of a given grant type while the Server is in use.

func (*Server) Cleanup added in v0.2.1

func (s *Server) Cleanup()

Cleanup should be executed when the Server is not required anymore, typically at app shutdown. Cleanup frees resources used by the Server.

func (*Server) HandleAuthorizationCodeAuthorizationRequest

func (s *Server) HandleAuthorizationCodeAuthorizationRequest(w http.ResponseWriter, r *http.Request) error

HandleAuthorizationCodeAuthorizationRequest handles the initial user authorization of scopes and returns a code. This is step 1 of 2.

func (*Server) HandleAuthorizationCodeTokenRequest

func (s *Server) HandleAuthorizationCodeTokenRequest(w http.ResponseWriter, r *http.Request) error

HandleAuthorizationCodeTokenRequest exchanges a code for an access token. This is step 2 of 2.

func (*Server) HandleClientCredentialsRequest

func (s *Server) HandleClientCredentialsRequest(w http.ResponseWriter, r *http.Request) error

HandleClientCredentialsRequest expects a POST request sending client ID and client secret of a client and, in case of correct credentials, exchanges them for an access token.

func (*Server) HandleDeviceCodeAuthorizationRequest

func (s *Server) HandleDeviceCodeAuthorizationRequest(w http.ResponseWriter, r *http.Request) error

HandleDeviceCodeAuthorizationRequest handles the request to initiate the device code flow by returning the device code, the user code and a validation URL. This is step 1 of 3.

func (*Server) HandleDeviceCodeTokenRequest

func (s *Server) HandleDeviceCodeTokenRequest(w http.ResponseWriter, r *http.Request) error

HandleDeviceCodeTokenRequest exchanges a device code for an access token. This is step 3 of 3.

func (*Server) HandleDeviceCodeUserAuthorization

func (s *Server) HandleDeviceCodeUserAuthorization(w http.ResponseWriter, r *http.Request) error

HandleDeviceCodeUserAuthorization displays a template that allows the user authorize or cancel the request. This is step 2 of 3.

func (*Server) HandleImplicitAuthorizationRequest

func (s *Server) HandleImplicitAuthorizationRequest(w http.ResponseWriter, r *http.Request) error

func (*Server) HandleResourceOwnerPasswordCredentialsRequest

func (s *Server) HandleResourceOwnerPasswordCredentialsRequest(w http.ResponseWriter, r *http.Request) error

HandleResourceOwnerPasswordCredentialsRequest expects a POST request sending username and password of a resource owner and, in case of correct credentials, exchanges them for an access token.

func (*Server) HandleUserLogin

func (s *Server) HandleUserLogin(w http.ResponseWriter, r *http.Request) error

HandleUserLogin displays the login template on a GET request and handles the login process on a POST request. On success, HandleUserLogin sets a session cookie and saves the session, linked to the user.

func (*Server) HandleUserLogout

func (s *Server) HandleUserLogout(w http.ResponseWriter, r *http.Request) error

HandleUserLogout reads the session cookie and removes the session linked to the user, effectively logging the user out.

func (*Server) HasGrantType

func (s *Server) HasGrantType(gt types.GrantType) bool

func (*Server) RemoveGrantType

func (s *Server) RemoveGrantType(gt types.GrantType)

RemoveGrantType removes the given grant type from the current list of enabled grant types for the server s. You can use this call to change the availability of a given grant type while the Server is in use.

func (*Server) ResetGrantTypes

func (s *Server) ResetGrantTypes()

ResetGrantTypes empties the internal list of enabled grant types.

type Session

type Session struct {
	// CookieName represents the name of the cookie to be set for storing the session ID.
	CookieName string
	// HTTPOnly specifies whether the session cookie has the HTTPOnly flag set.
	HTTPOnly bool
	// Secure specifies whether the session cookie has the Secure flag set (only for HTTPS).
	Secure bool
}

Session contains session and cookie settings.

type Storage

type Storage struct {
	// DeviceCodeRequestStorage stores requests for the Device Code Grant. Must be set for Device Code Grant.
	DeviceCodeRequestStorage storage.DeviceCodeStorage
	// AuthorizationCodeRequestStorage stores requests for the Authorization Code Grant.
	AuthorizationCodeRequestStorage storage.AuthorizationCodeRequestStorage
	// SessionStorage stores active user sessions. Required for all redirect-based grant flows.
	SessionStorage storage.SessionStorage
	// UserStorage stores user information and credentials. Required for all flows but the Client Credentials Grant flow.
	UserStorage storage.UserStorage
	// ClientStorage stores client information. Required for all grant flows.
	ClientStorage storage.ClientStorage
	// TokenStorage stores tokens, refresh tokens and related information. Required for all grant flows.
	TokenStorage storage.TokenStorage
}

Storage contains the storage implementations required for operations.

type Templates

type Templates struct {
	// Login represents the login HTML template for redirect based flows.
	Login []byte
	// AuthorizationCode represents the authorization page shown to the user when authorizing using the Authorization Code Grant.
	AuthorizationCode []byte
	// ImplicitGrant represents the authorization page shown to the user when authorizing using the Implicit Grant.
	ImplicitGrant []byte
	// DeviceCode represents the authorization page shown to the user when authorizing using the Device Code Grant.
	DeviceCode []byte
	//Though PKCE is based on the Authorization Code Grant, you can still choose a different template.
	PKCE []byte
	//Though OIDC is based on the Authorization Code Grant, you can still choose a different template.
	OIDC []byte
}

Templates contains the HTML templates displayed for the user.

type URLs

type URLs struct {
	// Login is the target URL for the user login page, e.g. /user_login.
	Login string
	// Logout is the target URL for the user logout page, e.g. /user_logout.
	Logout string
	// DeviceCode is the target URL for the user Device Code user authorization page.
	DeviceCode string
	// AuthorizationCode is the target URL for the Authorization Code user authorization page.
	AuthorizationCode string
	// Implicit is the target URL for the Implicit Grant user authorization page.
	Implicit string
}

URLs contains paths and/or URLs to the endpoints/routes defined by the caller. If you only use Client Credentials Grant + Resource Owner Password Credentials Grant, no URLs need to be set, since these grant flows are not redirect-based.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL