Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow internalClient to accept context.Context and account IDs as int #16

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 40 additions & 48 deletions authn/authn.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,29 @@
package authn

import (
"errors"
"context"
"net/http"
"time"
"strconv"

jwt "gopkg.in/square/go-jose.v2/jwt"
)

// TODO: jose/jwt references are all over the place. Refactor possible?

// ErrInvalidOptions is returned by SubjectFrom if invalid options are used
var ErrInvalidOptions = errors.New("invalid options for SubjectFrom")

// Client provides JWT verification for ID tokens generated by the AuthN server. In the future it
// will also implement the server's private APIs (aka admin actions).
type Client struct {
config Config
iclient *internalClient
kchain *keychainCache
verifier JWTClaimsExtractor
cli *Client2
}

// NewClient returns an initialized and configured Client.
func NewClient(config Config) (*Client, error) {
var err error
config.setDefaults()

ac := Client{}

ac.config = config

ac.iclient, err = newInternalClient(config.PrivateBaseURL, config.Username, config.Password)
cli, err := NewClient2(config)
if err != nil {
return nil, err
}

ac.kchain = newKeychainCache(time.Duration(config.KeychainTTL)*time.Minute, ac.iclient)
ac.verifier, err = NewIDTokenVerifier(config.Issuer, config.Audience, ac.kchain)
if err != nil {
return nil, err
}

return &ac, nil
return &Client{cli}, nil
}

// SubjectFrom will return the subject inside the given idToken if and only if the token is a valid
Expand All @@ -51,71 +32,82 @@ func NewClient(config Config) (*Client, error) {
//
// If the JWT does not verify, the returned error will explain why. This is for debugging purposes.
func (ac *Client) SubjectFrom(idToken string) (string, error) {
return ac.subjectFromVerifier(idToken, ac.verifier)
return ac.cli.SubjectFrom(idToken)
}

// SubjectFromWithAudience works like SubjectFrom but allows specifying a different
// JWT audience
func (ac *Client) SubjectFromWithAudience(idToken string, audience jwt.Audience) (string, error) {
verifier, err := newIDTokenVerifierWithAudiences(ac.config.Issuer, audience, ac.kchain)
if err != nil {
return "", err
}

return ac.subjectFromVerifier(idToken, verifier)
}

func (ac *Client) subjectFromVerifier(idToken string, verifier JWTClaimsExtractor) (string, error) {
claims, err := verifier.GetVerifiedClaims(idToken)
if err != nil {
return "", err
}
return claims.Subject, nil
return ac.cli.SubjectFromWithAudience(idToken, audience)
}

// GetAccount gets the account with the associated id
func (ac *Client) GetAccount(id string) (*Account, error) { //Should this be a string or an int?
return ac.iclient.GetAccount(id)
accountID, err := strconv.Atoi(id)
if err != nil {
return nil, err
}
return ac.cli.GetAccount(context.Background(), accountID)
}

// Update updates the account with the associated id
func (ac *Client) Update(id, username string) error {
return ac.iclient.Update(id, username)
accountID, err := strconv.Atoi(id)
if err != nil {
return err
}
return ac.cli.Update(context.Background(), accountID, username)
}

// LockAccount locks the account with the associated id
func (ac *Client) LockAccount(id string) error {
return ac.iclient.LockAccount(id)
accountID, err := strconv.Atoi(id)
if err != nil {
return err
}
return ac.cli.LockAccount(context.Background(), accountID)
}

// UnlockAccount unlocks the account with the associated id
func (ac *Client) UnlockAccount(id string) error {
return ac.iclient.UnlockAccount(id)
accountID, err := strconv.Atoi(id)
if err != nil {
return err
}
return ac.cli.UnlockAccount(context.Background(), accountID)
}

// ArchiveAccount archives the account with the associated id
func (ac *Client) ArchiveAccount(id string) error {
return ac.iclient.ArchiveAccount(id)
accountID, err := strconv.Atoi(id)
if err != nil {
return err
}
return ac.cli.ArchiveAccount(context.Background(), accountID)
}

// ImportAccount imports an account with the provided information, returns the imported account id
func (ac *Client) ImportAccount(username, password string, locked bool) (int, error) {
return ac.iclient.ImportAccount(username, password, locked)
return ac.cli.ImportAccount(context.Background(), username, password, locked)
}

// ExpirePassword expires the password of the account with the associated id
func (ac *Client) ExpirePassword(id string) error {
return ac.iclient.ExpirePassword(id)
accountID, err := strconv.Atoi(id)
if err != nil {
return err
}
return ac.cli.ExpirePassword(context.Background(), accountID)
}

// ServiceStats gets the http response object from calling the service stats endpoint
func (ac *Client) ServiceStats() (*http.Response, error) {
return ac.iclient.ServiceStats()
return ac.cli.ServiceStats(context.Background())
}

// ServerStats gets the http response object from calling the server stats endpoint
func (ac *Client) ServerStats() (*http.Response, error) {
return ac.iclient.ServerStats()
return ac.cli.ServerStats(context.Background())
}

// DefaultClient can be initialized by Configure and used by SubjectFrom.
Expand Down
116 changes: 116 additions & 0 deletions authn/client_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package authn

import (
"context"
"net/http"
"time"

jwt "gopkg.in/square/go-jose.v2/jwt"
)

// Client2 provides JWT verification for ID tokens generated by the AuthN server.
// It also implements the server's private APIs (aka admin actions)
type Client2 struct {
config Config
iclient *internalClient
kchain *keychainCache
verifier JWTClaimsExtractor
}

// NewClient2 returns an initialized and configured Client2
func NewClient2(config Config) (*Client2, error) {
config.setDefaults()

cli, err := newInternalClient(config.PrivateBaseURL, config.Username, config.Password)
if err != nil {
return nil, err
}

ttl := time.Duration(config.KeychainTTL) * time.Minute
kchain := newKeychainCache(ttl, cli)

verifier, err := NewIDTokenVerifier(config.Issuer, config.Audience, kchain)
if err != nil {
return nil, err
}

return &Client2{
config: config,
iclient: cli,
kchain: kchain,
verifier: verifier,
}, nil
}

// SubjectFrom will return the subject inside the given idToken if and only if the token is a valid
// JWT that passes all verification requirements. The returned value is the AuthN server's account
// ID and should be used as a unique foreign key in your users data.
//
// If the JWT does not verify, the returned error will explain why. This is for debugging purposes.
func (ac *Client2) SubjectFrom(idToken string) (string, error) {
return ac.subjectFromVerifier(idToken, ac.verifier)
}

// SubjectFromWithAudience works like SubjectFrom but allows specifying a different
// JWT audience
func (ac *Client2) SubjectFromWithAudience(idToken string, audience jwt.Audience) (string, error) {
verifier, err := newIDTokenVerifierWithAudiences(ac.config.Issuer, audience, ac.kchain)
if err != nil {
return "", err
}

return ac.subjectFromVerifier(idToken, verifier)
}

func (ac *Client2) subjectFromVerifier(idToken string, verifier JWTClaimsExtractor) (string, error) {
claims, err := verifier.GetVerifiedClaims(idToken)
if err != nil {
return "", err
}
return claims.Subject, nil
}

// GetAccount gets the account with the associated id
func (ac *Client2) GetAccount(ctx context.Context, id int) (*Account, error) {
return ac.iclient.GetAccount(ctx, id)
}

// Update updates the account with the associated id
func (ac *Client2) Update(ctx context.Context, id int, username string) error {
return ac.iclient.Update(ctx, id, username)
}

// LockAccount locks the account with the associated id
func (ac *Client2) LockAccount(ctx context.Context, id int) error {
return ac.iclient.LockAccount(ctx, id)
}

// UnlockAccount unlocks the account with the associated id
func (ac *Client2) UnlockAccount(ctx context.Context, id int) error {
return ac.iclient.UnlockAccount(ctx, id)
}

// ArchiveAccount archives the account with the associated id
func (ac *Client2) ArchiveAccount(ctx context.Context, id int) error {
return ac.iclient.ArchiveAccount(ctx, id)
}

// ImportAccount imports an account with the provided information, returns the imported account id
func (ac *Client2) ImportAccount(ctx context.Context, username, password string, locked bool) (int, error) {
return ac.iclient.ImportAccount(ctx, username, password, locked)
}

// ExpirePassword expires the password of the account with the associated id
func (ac *Client2) ExpirePassword(ctx context.Context, id int) error {
return ac.iclient.ExpirePassword(ctx, id)
}

// ServiceStats gets the http response object from calling the service stats endpoint
func (ac *Client2) ServiceStats(ctx context.Context) (*http.Response, error) {
return ac.iclient.ServiceStats(ctx)
}

// ServerStats gets the http response object from calling the server stats endpoint
func (ac *Client2) ServerStats(ctx context.Context) (*http.Response, error) {
return ac.iclient.ServerStats(ctx)
}
Loading