Categorygithub.com/scitokens/scitokens-go
modulepackage
0.3.2
Repository: https://github.com/scitokens/scitokens-go.git
Documentation: pkg.go.dev

# README

scitokens-go Go Reference

WORK IN PROGRESS library for handling SciTokens and WLCG tokens from Go, based on github.com/lestrrat-go/jwx libraries.

Included is a scitoken-validate command-line tool that uses the library to print and validate SciTokens with various criteria, see its README for installation and usage documentation.

The Enforcer API is believed to be stable, but breaking changes may still occur until version 1.0.0 is released.

Usage

To fetch and add the library to your Go project dependencies, run:

go get github.com/scitokens/scitokens-go

Then import it in your source:

import (
	scitokens "github.com/scitokens/scitokens-go"
)

Parsing SciTokens

Note: if you're only interested in validating tokens in a service, you can skip to the next section Validating Tokens, since the Enforcer abstracts away these details.

The SciToken interface is a light wrapper around the general Token interface from the github.com/lestrrat-go/jwx/jwt package, providing convenience methods for parsing and accessing SciToken-specific claims. After parsing the token into a jwt.Token you can convert it to an object implementing the SciToken interface with NewSciToken().

// PrintSciToken prints SciToken information to stdout, without doing any
// verification or validation of the token or its claims.
func PrintSciToken(tok []byte) error {
	jt, err := jwt.Parse(tok)
	if err != nil {
		return err
	}
	st, err := scitokens.NewSciToken(jt)
	if err != nil {
		return err
	}
	fmt.Println(st.Subject())
	fmt.Println(st.Issuer())
	fmt.Println(st.Scopes())
	fmt.Println(st.Groups())
	return nil
}

Validating Tokens

The Enforcer interface defines methods used to verify and validate tokens. Instantiate an enforcer with either NewEnforcer() for one-shot/throwaway use, or NewEnforcerDaemon() for long-lived processes. Both require one or more supported issuer URLs, and NewEnforcerDaemon additionally requires a context that defines the lifetime of the Enforcer and its background goroutines.

enf, err := scitokens.NewEnforcer("https://example.com")
if err != nil {
	log.Fatalf("failed to initialize enforcer: %s", err)
}

An enforcer instantiated with NewEnforcer() will fetch signing keys on-demand when a token is validated.

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
enf, err := scitokens.NewEnforcerDaemon(ctx, "https://example.com")
if err != nil {
	log.Fatalf("failed to initialize enforcer: %s", err)
}

An Enforcer instantiated with NewEnforcerDaemon() will fetch and cache the signing keys from the issuer, and start a goroutine that will routinely refresh the keys until the context is cancelled. Additional issuers can be added later with AddIssuer().

The enforcer provides a ValidateToken() method that verifies and validates a raw encoded token, and several convenience methods for validating tokens from a number of sources, including HTTP requests (ValidateTokenRequest()), and the execution environment (ValidateTokenEnvironment()).

By default the enforcer will verify that the token was signed by a trusted issuer, and that it passes basic validation criteria such as dates. It is not possible to directly parse a SciToken without performing these basic validation checks, this is by design, although it could change in the future if there is a good use case (the ValidateToken... function signatures and behavior won't change though).

Additional validation criteria can be attached to the enforcer with the following methods:

  • RequireAudience(), which takes a string representing the service's URL or some other identifier. It will check the tokens have this audience, or one of the standard wildcard audiences, currently "ANY" or "https://wlcg.cern.ch/jwt/v1/any". If not specified, the token audience will not be checked at all.

  • RequireScope(), which takes a Scope object that the token must have in the scope claim (pathed scopes will match exactly or for a hierarchical parent).

  • RequireGroup(), which takes a group name that must be in the wlcg.groups claim (group name must match exactly, but the leading slash is optional).

if err := enf.RequireAudience("https://example.com"); err != nil {
	log.Fatal(err)
}
if err := enf.RequireScope(scitokens.Scope{"compute.read", ""}); err != nil {
	log.Fatal(err)
}
if err := enf.RequireGroup("cms"); err != nil {
	log.Fatal(err)
}

Criteria set this way will apply to all future ValidateToken... calls. It's also possible to pass additional request-specific validation criteria to the ValidateToken... functions.

if _, err := enf.ValidateToken(tok, scitokens.WithGroup("cms/production")); err == nil {
	doRequest()
} else {
	e := &scitokens.TokenValidationError{}
	if !errors.As(err, &e) {
		// some internal error while parsing/validating the token
		log.Error(err)
	} else {
		// token is not valid, err (and e.Err) will say why.
		log.Debugf("access dened: %v", err)
	}
	denyRequest(err)
}

This example also demonstrates using errors.As() to check if the returned error is specifically a TokenValidationError due to the token not meeting some criteria, or some other internal error, which you may want to handle differently.

The ValidateToken... functions return a SciToken that can be inspected directly or passed to Validate() to test different criteria.

// request is valid if token has either /cms/production or /cms/operations group
if st, err := enf.ValidateToken(tok, scitokens.WithGroup("cms/production")); err == nil {
	doRequest()
} else if enf.Validate(st, scitokens.WithGroup("cms/operations")) == nil {
	doRequest()
} else {
	denyRequest(err)
}

# Packages

No description provided by the author
No description provided by the author

# Functions

GetGroups parses the wlcg.groups claim and returns a list of all groups, or an empty list if the wlcg.groups claim is missing.
GetScopes parses the scope claim and returns a list of all scopes, or an empty list if the scope claim is missing.
GetVersion retrieves the ver claim, or an empty string if the claim is missing.
NewEnforcer initializes a new enforcer for validating SciTokens from the provided issuer(s).
NewEnforcerDaemon initializes a new enforcer for validating SciTokens from the provided issuer(s), caching and refreshing keys periodically.
NewSciToken wraps a jwt.Token, populating the SciToken from the custom claims.
ParseScope parses a scope string like AUTHZ[:PATH].
PrintToken pretty-prints the token claims to w.
WithAudience validates that the token has the given audience or one of the supported "any" audiences, as defined in the AnyAudiences package variable.
WithGroup validates that the token contains the group (exactly, leading slash optional) in wlcg.groups.
WithScope validates that the token is allowed to perform the scopes operation on the scopes path or a sub-path.

# Variables

AnyAudiences is the list of special wildcard audiences that a token can present to be used anywhere that otherwise accepts it.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

Scope represents a token authorization scope, with optional path.
No description provided by the author

# Interfaces

Enforcer verifies that SciTokens https://scitokens.org are valid, from a certain issuer, and that they allow the requested resource.
SciToken wraps a standard JWT token to add custom claims.
Validator describes the interface to validate a SciToken.