tigasdk

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jan 4, 2021 License: MIT Imports: 19 Imported by: 0

README

Tiga SDK for Golang

Default Go Report Card Version

Golang SDK for the Tiga OIDC engine

Install

go get -u github.com/absurdlab/tiga-go-sdk

The SDK relies on:

  1. github.com/imulab/coldcall to make HTTP calls
  2. gopkg.in/square/go-jose.v2 to handle cryptography.

Get started

The main entrypoint of the SDK is the SDK object. To create it:

import (
    tigasdk "github.com/absurdlab/tiga-go-sdk"
)

var sdk = tigasdk.New(
    tigasdk.WithClientSecretBasic("example_client", "example_secret"),
)
HTTP Middleware

It is very easy to create an HTTP Middleware (i.e. func(http.Handler) http.Handler) to protect your endpoints.

httpMiddleware := sdk.Protect(tigasdk.ProtectOpt{
    Scopes: []string{"my_required_scope"},
    Leeway: 5 * time.Second,
})
Token endpoints

To execute the various token endpoint flows:

// client credentials flow
sdk.TokenByClientCredentials(ctx, []string{"my_scope"})

// authorization code flow (token endpoint leg)
sdk.TokenByCode(ctx, "auth_code", "https://redirect_uri", []string{"granted_scope"})

// exchange refresh token
sdk.TokenByRefreshToken(ctx, "refresh_token", []string{"granted_scope"})
Interaction providers

The SDK makes it easy for interaction providers (a special Tiga client) to interact with Tiga.

challenge := "challenge_parameter_from_url"

// Get state for login challenge
state, _ := sdk.LoginState(ctx, challenge)
// Post provider login response back to Tiga
sdk.LoginCallback(ctx, challenge, &tigasdk.LoginCallback{...})

// Get state for select account challenge
state, _ := sdk.SelectAccountState(ctx, challenge)
// Post provider select account response back to Tiga
sdk.SelectAccountCallback(ctx, challenge, &tigasdk.SelectAccountCallback{...})

// Get state for consent challenge
state, _ := sdk.ConsentState(ctx, challenge)
// Post provider consent response back to Tiga
sdk.ConsentCallback(ctx, challenge, &tigasdk.ConsentCallback{...})

// Render HTTP response to redirect browser back to Tiga at any point.
sdk.ResumeAuthorize(httpResponseWriter, httpRequest, challenge)

Documentation

Index

Constants

View Source
const AccessTokenType = "Bearer"
View Source
const DefaultServiceBaseURL = "https://sso.elan-vision.com"

Variables

View Source
var (
	ErrAccessTokenNotSet   = errors.New("access token not set on context")
	ErrMalformedAuthHeader = errors.New("authorization header is malformed")
	ErrInvalidAccessToken  = errors.New("access token is invalid")
	ErrInsufficientScope   = errors.New("insufficient scope")
)
View Source
var (
	// DefaultHTTPClient is the default http.Client used by the SDK if none is set. It uses a 10 second
	// timeout setting, does not follow redirects and skip TLS verification.
	DefaultHTTPClient = &http.Client{
		Timeout: 10 * time.Second,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}
	// WithServiceBaseURL sets the base Tiga url used by the sdk. By default,
	// DefaultServiceBaseURL is used.
	WithServiceBaseURL = func(url string) Option {
		return func(sdk *SDK) {
			sdk.serviceBaseURL = strings.TrimSuffix(url, "/")
		}
	}

	// WithClientSecretBasic sets the client id and client secret on the sdk object. The sdk will
	// use client_secret_basic method when requesting token endpoint.
	WithClientSecretBasic = func(clientId string, clientSecret string) Option {
		return func(sdk *SDK) {
			sdk.clientId = clientId
			sdk.clientSecret = clientSecret
			sdk.authMethod = oidc.ClientSecretBasic
		}
	}

	// WithClientSecretPost sets the client id and client secret on the sdk object. The sdk will
	// use client_secret_post method when requesting token endpoint.
	WithClientSecretPost = func(clientId string, clientSecret string) Option {
		return func(sdk *SDK) {
			sdk.clientId = clientId
			sdk.clientSecret = clientSecret
			sdk.authMethod = oidc.ClientSecretPost
		}
	}

	// WithPrivateKeyJwt sets the client id, client jwks and the signature algorithm to use
	// to sign the client_assertion parameter. The sdk will use private_key_jwt method when
	// requesting token endpoint.
	WithPrivateKeyJwt = func(clientId string, clientJwks *jwx.KeySet, signingAlg string) Option {
		return func(sdk *SDK) {
			sdk.clientId = clientId
			sdk.clientJwks = clientJwks
			sdk.authSigAlg = signingAlg
		}
	}

	// WithClientJwks sets the client jwks for the sdk.
	WithClientJwks = func(clientJwks *jwx.KeySet) Option {
		return func(sdk *SDK) {
			sdk.clientJwks = clientJwks
		}
	}

	// WithHTTPClient set the http client used by the sdk to make http request.
	// By default, if nothing is set, the sdk uses a default http client with
	// 10 second timeout and skips tls verification.
	WithHTTPClient = func(httpClient *http.Client) Option {
		return func(sdk *SDK) {
			sdk.httpClient = httpClient
		}
	}
)
View Source
var (
	ErrContextTooLarge = errors.New("context data exceeds discovery limit")
)
View Source
var (
	ErrUnexpectedResponse = errors.New("sdk received unexpected response")
)

Functions

func Protect

func Protect(discovery *oidc.Discovery, jwks *jwx.KeySet, opt *ProtectOpt) func(http.Handler) http.Handler

Protect returns a HTTP middleware to require access token issued by Tiga service in order to access the resource. This function assumes the caller holds oidc.Discovery and the verifying jwx.KeySet.

Types

type AccessToken

type AccessToken struct {
	Value          string
	Type           string
	ExpiresIn      int64
	ClientId       string
	Scopes         []string
	UserInfoClaims map[string]interface{}
}

AccessToken is the inflated representation of an access token.

func GetAccessToken

func GetAccessToken(ctx context.Context) (*AccessToken, error)

GetAccessToken retrieves the grant.AccessToken from the context. If no token was set on context, or the object set on context was not grant.AccessToken, ErrAccessTokenNotSet is returned as error.

type AccessTokenClaims

type AccessTokenClaims struct {
	jwt.Claims
	Client   string                 `json:"client"`
	Scope    string                 `json:"scope"`
	UserInfo map[string]interface{} `json:"userinfo,omitempty"`
}

AccessTokenClaims is the payload of a JWT encoded AccessToken issued by Tiga.

func (*AccessTokenClaims) Get

func (c *AccessTokenClaims) Get(name string) (interface{}, bool)

type Authentication

type Authentication struct {
	Subject  string          `json:"subject"`
	IdpId    string          `json:"idp_id"`
	AuthTime int64           `json:"auth_time"`
	Context  json.RawMessage `json:"context,omitempty"`
}

Authentication is a End-User authentication record.

type Client

type Client struct {
	Name              string   `json:"name"`
	Contacts          []string `json:"contacts,omitempty"`
	LogoURI           string   `json:"logo_uri,omitempty"`
	PolicyURI         string   `json:"policy_uri,omitempty"`
	ClientURI         string   `json:"client_uri,omitempty"`
	TermsOfServiceURI string   `json:"tos_uri,omitempty"`
}

Client contains the publicly displayable data of the client.

type ConsentCallback

type ConsentCallback struct {
	InteractionCallback

	// GrantedScopes are the list of scopes that the user has granted access
	// to the client. The OP must verify that these scopes had indeed been
	// requested. Scopes included here are those the End-User had explicitly
	// granted access to, no matter their previous status. These scopes will
	// be marked as granted in the session.
	GrantedScopes []string `json:"granted_scopes"`

	// RejectedScopes are the list of scopes that the user has rejected. The OP
	// must verify that these scopes had indeed been requested.  Scope included
	// here are those the End-User had explicitly rejected access to, for instance,
	// by pressing the "Deny" button, or by unchecking the checkbox. These scopes
	// will be marked as rejected in the session.
	//
	// If a scope is both granted and rejected, rejection takes precedence.
	RejectedScopes []string `json:"rejected_scopes"`

	// Ephemeral marks this response as one-time only. By default, granted scopes
	// will be recorded and contributes to silent grant on subsequent requests.
	// By marking response as ephemeral, OP skips persistence.
	Ephemeral bool `json:"ephemeral"`
}

ConsentCallback is the request payload of a callback made by consent interaction provider.

type ErrorResponse

type ErrorResponse struct {
	Status int    `json:"status"`
	Code   string `json:"error,omitempty"`
	Reason string `json:"error_description,omitempty"`
}

ErrorResponse is the response object when error has occurred at token endpoint.

func (*ErrorResponse) Error

func (r *ErrorResponse) Error() string

type InteractionCallback

type InteractionCallback struct {
	// Success is an indicator on if the interaction was positively successful.
	// If successful, operational data will be read from payload; otherwise,
	// error description will be read.
	Success bool `json:"success"`

	// Timestamp is the UNIX timestamp of the indicated event.
	Timestamp int64 `json:"timestamp"`

	// Nonce is the nonce parameter passed to the interaction provider
	// on the initial redirect. OP requires this piece of data in order
	// to prevent replay.
	Nonce string `json:"nonce"`

	// Error is the IDP specific error code indicating interaction failure.
	// It will not be returned to the client. The OP logs it for
	// debugging and auditing purposes.
	Error string `json:"error"`

	// ErrorDescription is the IDP specific description for the error
	// code. It will not be returned to the client. The OP logs it for
	// debugging and auditing purposes.
	ErrorDescription string `json:"error_description"`
}

InteractionCallback is the common elements in an interaction callback request.

type InteractionState

type InteractionState struct {
	// Subject is the subject identifier for the logged in End-User.
	// This field is optional when the login user had not be determined.
	// If OP had confirmed a login, this field will be shown along with
	// a Authentications map containing only that subject.
	Subject string `json:"subject,omitempty"`

	// Authentications contain zero or more candidates to be considered for
	// login. Login providers shall expect an empty map. Select account
	// providers shall expect a map containing one or more items. Consent
	// providers shall expect a single entry map whose key matches the Subject.
	Authentications map[string]*Authentication `json:"authentications"`

	// Scopes is the map of the requested scopes and their corresponding textual
	// status (granted|pending|rejected) and displayable detail. OP recommends
	// the consent provider display all scopes and their corresponding status to
	// the End-User so a conscious decision could be made. However, the consent
	// provider is at liberty to display any combination of the three categories.
	// Note scopes will not be shared unless Subject is determined.
	Scopes map[string]ScopeData `json:"scopes,omitempty"`

	// Client is the public sharable data about the client who made the initial
	// authorize request. OP encourages display of these data so the End-User is
	// aware which party are they granting access to.
	Client *Client `json:"client"`

	// OIDC is the public sharable data about the request itself. Interaction
	// providers are expected to adhere to the request parameters with their
	// best effort.
	OIDC *OIDC `json:"oidc"`
}

InteractionState contains all data that an interaction provider may wish to know about the current state of the End-User interaction.

Some data are selectively not exposed at certain stage. For instance, before subject is confirmed, scope data is not exposed. And when subject is confirmed, all other candidates authentication sessions are hidden. Client and OIDC data is always returned.

type LoginCallback

type LoginCallback struct {
	InteractionCallback

	// Subject is the IDP unique identifier for the logged in user.
	Subject string `json:"subject"`

	// Amr is the list of authentication methods
	Amr []string `json:"amr"`

	// Acr is the IDP determined authentication context
	// reference.
	Acr string `json:"acr"`

	// Context is the data that login provider wishes to
	// cache at the OP and shared back to it on the next
	// interaction involving this subject. Typical usage
	// includes user avatar, nickname, etc. The data in
	// this field will be size restricted to prevent abuse.
	//
	// Note that context is only meaningful when Remember
	// option is used. If not remembered, Context data is
	// lost.
	Context json.RawMessage `json:"context"`

	// Remember is the number of seconds to remember this
	// authentication, which directly translates to the
	// authentication session validity period. If it is
	// non-positive, it will be treated as not remembered.
	Remember int64 `json:"remember"`
}

LoginCallback is the request payload of a callback made by login interaction provider.

type OIDC

type OIDC struct {
	Display   string `json:"display,omitempty"`
	UILocales string `json:"ui_locales,omitempty"`
	LoginHint string `json:"login_hint,omitempty"`
	AcrValues string `json:"acr_values,omitempty"`
}

OIDC contains the publicly displayable data of the OIDC request.

type Option

type Option func(sdk *SDK)

Option describes logic to configure the SDK

type ProtectOpt

type ProtectOpt struct {
	// Audience is the expected "aud" in the access token claims.
	// When empty or nil, "aud" validation is not performed.
	Audience []string

	// Subject is the expected "sub" in the access token claims.
	// When empty or nil, "sub" validation is not performed.
	Subject string

	// Scopes is the list of required scopes in the access token claims.
	// When empty or nil, "scope" validation is not performed.
	Scopes []string

	// Leeway is the time skew tolerance
	Leeway time.Duration

	// RenderError is the function that is called in case of error. If not
	// provided, the middleware just write 401 status.
	RenderError func(http.ResponseWriter, *http.Request, error)
}

ProtectOpt is the options for Protect middleware.

type SDK

type SDK struct {
	// contains filtered or unexported fields
}

SDK is the entrypoint of the kit.

func New

func New(options ...Option) *SDK

New creates a new sdk object to expose various features. A series of Option can be applied to customize its parameters.

func (*SDK) ConsentCallback

func (s *SDK) ConsentCallback(ctx context.Context, xid string, callback *ConsentCallback) (bool, error)

func (*SDK) ConsentState

func (s *SDK) ConsentState(ctx context.Context, xid string) (*InteractionState, error)

func (*SDK) Discovery

func (s *SDK) Discovery() *oidc.Discovery

Discovery returns a new copy of the underlying internal oidc.Discovery.

func (*SDK) LoginCallback

func (s *SDK) LoginCallback(ctx context.Context, xid string, callback *LoginCallback) (bool, error)

func (*SDK) LoginState

func (s *SDK) LoginState(ctx context.Context, xid string) (*InteractionState, error)

func (*SDK) Protect

func (s *SDK) Protect(opt *ProtectOpt) func(http.Handler) http.Handler

Protect returns a HTTP middleware to require access token issued by Tiga service in order to access the resource.

func (*SDK) ResumeAuthorize

func (s *SDK) ResumeAuthorize(rw http.ResponseWriter, r *http.Request, xid string)

func (*SDK) SelectAccountCallback

func (s *SDK) SelectAccountCallback(ctx context.Context, xid string, callback *SelectAccountCallback) (bool, error)

func (*SDK) SelectAccountState

func (s *SDK) SelectAccountState(ctx context.Context, xid string) (*InteractionState, error)

func (*SDK) TokenByClientCredentials

func (s *SDK) TokenByClientCredentials(ctx context.Context, scopes []string) (*TokenResponse, error)

func (*SDK) TokenByCode

func (s *SDK) TokenByCode(ctx context.Context, code string, redirectURI string, scopes []string) (*TokenResponse, error)

func (*SDK) TokenByRefreshToken

func (s *SDK) TokenByRefreshToken(ctx context.Context, refreshToken string, scopes []string) (*TokenResponse, error)

type ScopeData

type ScopeData struct {
	// Status is the textual description of spec.ScopeStatus.
	// See scopeStatusMap
	Status string `json:"status"`

	// Detail is the raw data of this scope, usually describing
	// the scope of access in various displayable locales. Scope
	// detail registered with the client takes precedence, followed
	// by the default detail data about some common scopes prepared
	// by OP.
	//
	// Client registration is expected to consult consent provider
	// about the format of this data, hence, the consent provider
	// should understand the data here.
	Detail json.RawMessage `json:"detail"`
}

ScopeData describes status and details about a scope.

type SelectAccountCallback

type SelectAccountCallback struct {
	InteractionCallback

	// SelectId is the id of the selected authentication.
	SelectedId string `json:"subject"`
}

SelectAccountCallback is the request payload of a callback made by select account interaction provider.

type Stub added in v0.4.0

type Stub interface {
	// TokenByClientCredentials acquire access tokens using the client_credentials flow.
	TokenByClientCredentials(ctx context.Context, scopes []string) (*TokenResponse, error)

	// TokenByCode acquire access tokens, and optionally refresh token and id_token using the authorization_code flow.
	TokenByCode(ctx context.Context, code string, redirectURI string, scopes []string) (*TokenResponse, error)

	// TokenByRefreshToken acquire access tokens and refresh token by exchanging in existing refresh token.
	TokenByRefreshToken(ctx context.Context, refreshToken string, scopes []string) (*TokenResponse, error)

	// LoginState gets the InteractionState of the login challenge.
	LoginState(ctx context.Context, xid string) (*InteractionState, error)

	// SelectAccountState gets the InteractionState of the select account challenge.
	SelectAccountState(ctx context.Context, xid string) (*InteractionState, error)

	// ConsentState gets the InteractionState of the consent challenge.
	ConsentState(ctx context.Context, xid string) (*InteractionState, error)

	// LoginCallback posts the End-User's LoginCallback response back to Tiga.
	LoginCallback(ctx context.Context, xid string, callback *LoginCallback) (bool, error)

	// SelectAccountCallback posts the End-User's SelectAccountCallback response back to Tiga.
	SelectAccountCallback(ctx context.Context, xid string, callback *SelectAccountCallback) (bool, error)

	// ConsentCallback posts the End-User's ConsentCallback response back to Tiga.
	ConsentCallback(ctx context.Context, xid string, callback *ConsentCallback) (bool, error)

	// ResumeAuthorize redirects the http response back to the authorize resume endpoint.
	ResumeAuthorize(rw http.ResponseWriter, r *http.Request, xid string)
}

Stub describes the client side functions provided by the SDK to communicate with the Tiga instance. It is included in the SDK so that client side code can implement it to mock Tiga during development and testing.

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token,omitempty"`
	TokenType    string `json:"token_type,omitempty"`
	ExpiresIn    *int64 `json:"expires_in,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
	IdToken      string `json:"id_token,omitempty"`
	Scope        string `json:"scope,omitempty"`
}

TokenResponse is the response object at token endpoint.

Directories

Path Synopsis
jwx
Package jwx provides a thin wrapper layer around gopkg.in/square/go-jose.v2 to provide JOSE related operations in the OpenID Connect context.
Package jwx provides a thin wrapper layer around gopkg.in/square/go-jose.v2 to provide JOSE related operations in the OpenID Connect context.
Package oidc provides OpenID Connect specification values and custom extensions.
Package oidc provides OpenID Connect specification values and custom extensions.

Jump to

Keyboard shortcuts

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