Documentation
¶
Index ¶
- Constants
- func CreateTestCert(t *testing.T) (string, string, error)
- func GenerateAndStoreTokenForSelfClient(tokenFile string) ([]byte, error)
- func GenerateToken() ([]byte, error)
- func GetStoredToken(tokenFile string) ([]byte, error)
- func GetUser(t *testing.T) (string, string)
- func IncludeAbortErrorsInBody(c *gin.Context)
- func Login(r *resty.Request, username, password string) (string, error)
- func LoginWithOKTA(r *resty.Request, username, token string) (string, error)
- func NewAuthenticatedClientRequest(url, cert, jwt string) *resty.Request
- func NewClientRequest(url, cert string) *resty.Request
- func QueryREST(router *gin.Engine, endpoint, extra string) (*httptest.ResponseRecorder, error)
- func RefreshJWT(url, cert, token string) (string, error)
- func StartTestServer(s StartStop, certPath, keyPath string) (string, func() error, error)
- func TokenDir() (string, error)
- func TokenMatches(input, expected []byte) bool
- func UserNameToUID(name string) (string, error)
- type AuthCallback
- type ClientCLI
- type Error
- type JWTPermissionsError
- type OktaUser
- type PasswordHandler
- type Server
- func (s *Server) AddOIDCRoutes(addr, issuer, clientID, clientSecret string)
- func (s *Server) AddStaticPage(staticFS embed.FS, rootDir, path string)
- func (s *Server) AllowedAccess(c *gin.Context, user string) bool
- func (s *Server) AuthRouter() *gin.RouterGroup
- func (s *Server) EnableAuth(certFile, keyFile string, acb AuthCallback) error
- func (s *Server) EnableAuthWithServerToken(certFile, keyFile, tokenBasename string, acb AuthCallback) error
- func (s *Server) GetUser(c *gin.Context) *User
- func (s *Server) Router() *gin.Engine
- func (s *Server) SetStopCallBack(cb StopCallback)
- func (s *Server) Start(addr, certFile, keyFile string) error
- func (s *Server) Stop()
- type StartStop
- type StdPasswordHandler
- type StopCallback
- type StringLogger
- type User
Constants ¶
const ( ErrBadJWTClaim = Error("JWT had bad claims") ErrEmailNotPresent = Error("field `email` not present") )
const ( ErrCouldNotVerifyToken = Error("token could not be verified") ErrOIDCUnexpectedState = Error("the state was not as expected") ErrOIDCMissingToken = Error("id token missing from OAuth2 token") ErrJSONValueNotString = Error("non-string value in JSON field") ErrOIDCBadMeta = Error("issuer meta information not found") )
const ( // EndPointREST is the base location for all REST endpoints. EndPointREST = "/rest/v1" // EndPointJWT is the endpoint for creating or refreshing a JWT. EndPointJWT = EndPointREST + "/jwt" // EndPointAuth is the name of the router group that endpoints requiring JWT // authorisation should belong to. EndPointAuth = EndPointREST + "/auth" // EndpointOIDCLogin will be handled by redirecting the user to Okta. EndpointOIDCLogin = "/login" // EndpointOIDCCLILogin will be handled by redirecting the user to Okta, // to get an auth code back to copy paste. EndpointOIDCCLILogin = "/login-cli" // EndpointAuthCallback is the endpoint where the OIDC provider will // send the user back to after login. EndpointAuthCallback = "/callback" EndpointAuthCLICallback = "/callback-cli" // EndpointCLIAuthCode is the endpoint the user can get an auth code from // to copy paste into the terminal for a CLI session. EndpointCLIAuthCode = "/auth-code" ErrNeedsAuth = Error("authentication must be enabled") )
const ClientProtocol = "https://"
const DevEnvKey = "GAS_DEV"
const DevEnvVal = "1"
const ErrBadQuery = Error("bad query")
const ErrNoAuth = Error("authentication failed")
Variables ¶
This section is empty.
Functions ¶
func CreateTestCert ¶ added in v1.0.2
CreateTestCert creates a self-signed cert and key in a temp dir and returns their paths.
func GenerateAndStoreTokenForSelfClient ¶ added in v1.1.0
GenerateAndStoreTokenForSelfClient calls GenerateToken() and returns the token, but also stores it in the given file, readable only by the current user. You could call this when starting a Server, and then in your AuthCallback verify a client trying to login by comparing their "password" against the token, using TokenMatches(). (Using EnableAuthWithServerToken() does all this for you.)
A command line client started by the same user that started the Server would then be able to login by getting the token using GetStoredToken(), and using that as its "password".
If the given tokenFile already exists, and contains a single 43 byte string, then that is re-used as the token instead.
func GenerateToken ¶ added in v1.1.0
GenerateToken creates a cryptographically secure pseudorandom URL-safe base64 encoded string 43 bytes long. Returns it as a byte slice.
func GetStoredToken ¶ added in v1.1.0
GetStoredToken reads the token from the given file but only returns it if it's got some length.
We also check if the file has private permissions, otherwise we won't use it. This is as an attempt to reduce the likelihood of the token being leaked with its long expiry time (used so the user doesn't have to continuously log in, as we're not working with specific refresh tokens to get new access tokens).
func IncludeAbortErrorsInBody ¶ added in v1.2.0
IncludeAbortErrorsInBody is a gin.HandlerFunc that can be Use()d with gin routers from Router() and AuthRouter() that ensures that the errors we accumulate in AbortWithError() calls get written to the returned body.
func Login ¶
Login is a client call to a Server listening at the domain:port url given to the request that checks the given password is valid for the given username, and returns a JWT if so.
Make the request using NewClientRequest() and a non-blank path to a certificate to force us to trust that certificate, eg. if the server was started with a self-signed certificate.
func LoginWithOKTA ¶
LoginWithOKTA sends a request to the server containing the token as a cookie, so it will be able to return the JWT for the user. The request should have been made with an addr that is just the domain:port that was used to Start() the server.
Make the request using NewClientRequest() and a non-blank path to a certificate to force us to trust that certificate, eg. if the server was started with a self-signed certificate.
func NewAuthenticatedClientRequest ¶
func NewAuthenticatedClientRequest(url, cert, jwt string) *resty.Request
NewAuthenticatedClientRequest is like NewClientRequest, but sets the given JWT in the authorization header.
func NewClientRequest ¶
func NewClientRequest(url, cert string) *resty.Request
NewClientRequest creates a resty Request that will trust the certificate at the given path. cert can be blank to only trust the normal installed cert chain.
func QueryREST ¶ added in v1.0.2
QueryREST does a test GET of the given REST endpoint (start it with /), with extra appended (start it with ?). router can be Server.Router().
func RefreshJWT ¶
RefreshJWT is like Login(), but refreshes a JWT previously returned by Login() if it's still valid.
func StartTestServer ¶ added in v1.0.2
StartTestServer starts the given server using the given cert and key paths and returns the address and a func you should defer to stop the server.
func TokenDir ¶ added in v1.3.0
TokenDir is the directory where the server will store a token file when using GenerateAndStoreTokenForSelfClient(), and ClientCLI will store JWTs. It is the value of XDG_STATE_HOME, falling back to the user's HOME directory.
func TokenMatches ¶ added in v1.1.0
TokenMatches compares two tokens and tells you if they match. Does so in a cryptographically secure way (avoiding timing attacks).
func UserNameToUID ¶ added in v1.0.1
UserNameToUID converts user name to UID.
Types ¶
type AuthCallback ¶
AuthCallback is a function that returns true if the given password is valid for the given username. It also returns the user's UID.
type ClientCLI ¶ added in v1.3.0
type ClientCLI struct {
// contains filtered or unexported fields
}
ClientCLI can be used by a CLI client to log in to a go-authserver Server.
func NewClientCLI ¶ added in v1.3.0
func NewClientCLI(jwtBasename, serverTokenBasename, url, cert string, oktaMode bool) (*ClientCLI, error)
NewClientCLI returns a ClientCLI that will get and store JWTs from and to a file with the given basename in the user's XDG_STATE_HOME or HOME directory, initially retrieving the JWT from the server at url using cert.
If the user needs to login (no valid JWT found), asks user for the password or an oktaCode if oktaMode is true.
The normal password checking procedure will be bypassed if the current user is the same one that started the server, the server used EnableAuthWithServerToken(), and the given serverTokenBasename file in XDG_STATE_HOME or HOME contains the server's token.
func (*ClientCLI) AuthenticatedRequest ¶ added in v1.3.0
NewAuthenticatedRequest logs in to our server if needed to get the jwt, and returns an authenticated request.
func (*ClientCLI) CanReadServerToken ¶ added in v1.3.0
CanReadServerToken returns true if this user can read the server token file and the token is the correct length. Does NOT check with the server if it's actually correct. Use this as a shortcut prior to trying to login for a CLI command that's only intended for use by the user who started a server.
func (*ClientCLI) GetJWT ¶ added in v1.3.0
GetJWT checks if we have stored a jwt in our file. If so, the JWT is refreshed and returned.
Otherwise, we ask the user for the password/code and login, storing and returning the new JWT.
func (*ClientCLI) Login ¶ added in v1.3.0
Login either asks the user for a password or for their okta code and logs in to our server with it. If this user started the server, gets the "password" from the server token file instead. If the optional username and password are supplied (for testing purposes), uses those instead of asking on the terminal.
type JWTPermissionsError ¶ added in v1.3.0
type JWTPermissionsError struct {
// contains filtered or unexported fields
}
JWTPermissionsError is used to distinguish this type of error - where the already stored JWT token doesn't have private permissions.
func (JWTPermissionsError) Error ¶ added in v1.3.0
func (e JWTPermissionsError) Error() string
Error is the print out string for JWTPermissionsError, so the user can see and rectify the permissions issue.
type OktaUser ¶
type OktaUser struct {
Email string `json:"email"`
}
OktaUser is used to json.Unmarshal Okta user claims.
type PasswordHandler ¶ added in v1.3.0
type PasswordHandler interface { Prompt(string, ...interface{}) ReadPassword() ([]byte, error) IsTerminal() bool }
PasswordHandler can ask for and return a password read from a reader.
type Server ¶
Server is used to start a web server that provides a REST API for authenticating, and a router you can add website pages to.
func New ¶
New creates a Server which can serve a REST API and website.
It logs to the given io.Writer, which could for example be syslog using the log/syslog pkg with syslog.new(syslog.LOG_INFO, "tag").
func (*Server) AddOIDCRoutes ¶
AddOIDCRoutes creates the OAuth environments for both the web app and the CLI and adds the login and callback endpoints, along with an endpoint to get an auth code for the CLI. Addr should be the same domain:port as later supplied to Start().
func (*Server) AddStaticPage ¶
AddStaticPage adds the given document root to the Router() at the given absolute query path. Files within the document root will then be served.
The files will be embedded by default, using the given embed.FS. You can create one of these by saying in your package: //go:embed static var staticFS embed.FS
For a live view of the files in a running server, set the env var GAS_DEV to 1.
func (*Server) AllowedAccess ¶ added in v1.3.0
AllowedAccess gets our current user if we have EnableAuth(), and returns true if that matches the given username. Always returns true if we have not EnableAuth(), or if our current user is the user who started the Server. If user is blank, it's a test if the current user started the Server.
func (*Server) AuthRouter ¶
func (s *Server) AuthRouter() *gin.RouterGroup
AuthRouter returns a router for authenticed end points. Use it to add end points that are sub-paths of EndPointAuth (providing just the relative path to eg. GET()). This will return nil until you've called EnableAuth().
func (*Server) EnableAuth ¶
func (s *Server) EnableAuth(certFile, keyFile string, acb AuthCallback) error
EnableAuth adds the /rest/v1/jwt POST and GET endpoints to the REST API.
The /rest/v1/jwt POST endpoint requires the username and password parameters in a form or as JSON. It passes these to the given auth callback, and if it returns true, a JWT is returned (as a JSON string) in the response that contains Username and UIDs (comma separated strings).
Alternatively, you can POST with an oktaCookieName cookie with a value of the okta auth code from the auth-code endpoint. If the code is valid, likewise returns a JWT. You'll also need to call AddOIDCRoutes() for this scheme to work.
Queries to endpoints that need authorisation should include the JWT in the authorization header as a bearer token. Those endpoints can be implemented by extracting the *User information out of the JWT using getUser().
JWTs are signed and verified using the given cert and key files.
GET on the endpoint will refresh the JWT. JWTs expire after 5 days, but can be refreshed up until day 10 from issue.
func (*Server) EnableAuthWithServerToken ¶ added in v1.3.0
func (s *Server) EnableAuthWithServerToken(certFile, keyFile, tokenBasename string, acb AuthCallback) error
EnableAuthWithServerToken is like EnableAuth(), but also stores the current username as the "server" user who can login with a server token that will be generated and stored in a file called tokenBasename in TokenDir(), instead of via auth callback or okta.
func (*Server) GetUser ¶ added in v1.0.1
GetUser retrieves the *User information extracted from the JWT in the auth header. This will only be present after calling EnableAuth(), on a route in the authGroup.
func (*Server) Router ¶
Router returns a router for non-authenticed end points. Use it to add end points that are sub-paths of EndPointREST (providing the full path to eg. GET()).
func (*Server) SetStopCallBack ¶
func (s *Server) SetStopCallBack(cb StopCallback)
func (*Server) Start ¶
Start will start listening to the given address (eg. "localhost:8080"), and serve the REST API and website over https; you must provide paths to your certficate and key file.
It blocks, but will gracefully shut down on SIGINT and SIGTERM. If you Start() in a go-routine, you can call Stop() manually.
type StdPasswordHandler ¶ added in v1.3.0
type StdPasswordHandler struct{}
StdPasswordHandler is the default password handler using stdout and stdin.
func (StdPasswordHandler) IsTerminal ¶ added in v1.3.0
func (p StdPasswordHandler) IsTerminal() bool
IsTerminal returns true if stdin is a terminal (and thus ReadPassword() is possible).
func (StdPasswordHandler) Prompt ¶ added in v1.3.0
func (p StdPasswordHandler) Prompt(msg string, a ...interface{})
Prompt outputs the given string to stdout, formatting it with any given vars.
func (StdPasswordHandler) ReadPassword ¶ added in v1.3.0
func (p StdPasswordHandler) ReadPassword() ([]byte, error)
ReadPassword reads a password from stdin.
type StopCallback ¶
type StopCallback func()
StopCallback is a function that you can give SetStopCallback() to have this function called when the Server is Stop()ped.
type StringLogger ¶
StringLogger is a thread-safe logger that logs to a string. Useful for testing Server logging.
func NewStringLogger ¶
func NewStringLogger() *StringLogger
NewStringLogger returns a new StringLogger.
func (*StringLogger) Reset ¶
func (s *StringLogger) Reset()
Reset passes through to our strings.Builder while being thread-safe.
func (*StringLogger) String ¶
func (s *StringLogger) String() string
String passes through to our strings.Builder while being thread-safe.