mirror of
https://github.com/nais/wonderwall.git
synced 2026-08-19 02:56:15 +00:00
refactor: token -> jwt for accuracy
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package token
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
@@ -28,7 +28,7 @@ func NewAccessToken(raw string, token jwt.Token) *AccessToken {
|
||||
}
|
||||
|
||||
func ParseAccessToken(raw string, jwks jwk.Set) (*AccessToken, error) {
|
||||
accessToken, err := ParseJwt(raw, jwks)
|
||||
accessToken, err := Parse(raw, jwks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package jwt
|
||||
|
||||
const (
|
||||
JtiClaim = "jti"
|
||||
SidClaim = "sid"
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package token
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -58,7 +58,7 @@ func NewIDToken(raw string, token jwt.Token) *IDToken {
|
||||
}
|
||||
|
||||
func ParseIDToken(raw string, jwks jwk.Set) (*IDToken, error) {
|
||||
idToken, err := ParseJwt(raw, jwks)
|
||||
idToken, err := Parse(raw, jwks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
)
|
||||
|
||||
func Parse(raw string, jwks jwk.Set) (jwt.Token, error) {
|
||||
parseOpts := []jwt.ParseOption{
|
||||
jwt.WithKeySet(jwks),
|
||||
jwt.InferAlgorithmFromKey(true),
|
||||
}
|
||||
token, err := jwt.ParseString(raw, parseOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing jwt: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func GetStringClaim(token jwt.Token, claim string) (string, error) {
|
||||
if token == nil {
|
||||
return "", fmt.Errorf("token is nil")
|
||||
}
|
||||
|
||||
gotClaim, ok := token.Get(claim)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("missing required '%s' claim in id_token", claim)
|
||||
}
|
||||
|
||||
claimString, ok := gotClaim.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("'%s' claim is not a string", claim)
|
||||
}
|
||||
|
||||
return claimString, nil
|
||||
}
|
||||
|
||||
func GetStringClaimOrEmpty(token jwt.Token, claim string) string {
|
||||
str, err := GetStringClaim(token, claim)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package jwt
|
||||
|
||||
type IDs struct {
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Tokens struct {
|
||||
IDToken *IDToken
|
||||
AccessToken *AccessToken
|
||||
}
|
||||
|
||||
func (in *Tokens) JwtIDs() IDs {
|
||||
return IDs{
|
||||
IDToken: in.IDToken.GetJtiClaim(),
|
||||
AccessToken: in.AccessToken.GetJtiClaim(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTokens(idToken *IDToken, accessToken *AccessToken) *Tokens {
|
||||
return &Tokens{
|
||||
IDToken: idToken,
|
||||
AccessToken: accessToken,
|
||||
}
|
||||
}
|
||||
|
||||
func ParseOauth2Token(tokens *oauth2.Token, jwks jwk.Set) (*Tokens, error) {
|
||||
idToken, ok := tokens.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing id_token in token response")
|
||||
}
|
||||
|
||||
return ParseTokensFromStrings(idToken, tokens.AccessToken, jwks)
|
||||
}
|
||||
|
||||
func ParseTokensFromStrings(idToken, accessToken string, jwks jwk.Set) (*Tokens, error) {
|
||||
parsedIdToken, err := ParseIDToken(idToken, jwks)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("id_token: %w", err)
|
||||
}
|
||||
|
||||
parsedAccessToken, err := ParseAccessToken(accessToken, jwks)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("access_token: %w", err)
|
||||
}
|
||||
|
||||
return NewTokens(parsedIdToken, parsedAccessToken), nil
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package jwt
|
||||
|
||||
type Type int
|
||||
|
||||
const (
|
||||
TypeIDToken Type = iota
|
||||
TypeAccessToken
|
||||
)
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/nais/wonderwall/pkg/config"
|
||||
"github.com/nais/wonderwall/pkg/cookie"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,7 +19,7 @@ const (
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
ExchangeToken(ctx context.Context, accessToken *token.AccessToken) (*TokenResponse, error)
|
||||
ExchangeToken(ctx context.Context, accessToken *jwt.AccessToken) (*TokenResponse, error)
|
||||
SetCookie(w http.ResponseWriter, token *TokenResponse, opts cookie.Options)
|
||||
HasCookie(r *http.Request) bool
|
||||
ClearCookie(w http.ResponseWriter, opts cookie.Options)
|
||||
@@ -47,7 +47,7 @@ type client struct {
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func (c client) ExchangeToken(ctx context.Context, accessToken *token.AccessToken) (*TokenResponse, error) {
|
||||
func (c client) ExchangeToken(ctx context.Context, accessToken *jwt.AccessToken) (*TokenResponse, error) {
|
||||
req, err := request(ctx, c.config.TokenURL, accessToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating request %w", err)
|
||||
@@ -99,7 +99,7 @@ func (c client) cookieOptions(opts cookie.Options) cookie.Options {
|
||||
WithSameSite(SameSiteMode)
|
||||
}
|
||||
|
||||
func request(ctx context.Context, url string, token *token.AccessToken) (*http.Request, error) {
|
||||
func request(ctx context.Context, url string, token *jwt.AccessToken) (*http.Request, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/nais/wonderwall/pkg/config"
|
||||
"github.com/nais/wonderwall/pkg/cookie"
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/loginstatus"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
func TestClient_ExchangeToken(t *testing.T) {
|
||||
@@ -25,18 +25,18 @@ func TestClient_ExchangeToken(t *testing.T) {
|
||||
client := loginstatus.NewClient(cfg, httpclient)
|
||||
|
||||
for _, test := range []struct {
|
||||
token *token.AccessToken
|
||||
token *jwt.AccessToken
|
||||
err error
|
||||
}{
|
||||
{
|
||||
token: token.NewAccessToken("valid-token", nil),
|
||||
token: jwt.NewAccessToken("valid-token", nil),
|
||||
},
|
||||
{
|
||||
token: token.NewAccessToken("invalid-token", nil),
|
||||
token: jwt.NewAccessToken("invalid-token", nil),
|
||||
err: fmt.Errorf("client error: HTTP: %d: %s: %s", http.StatusUnauthorized, "access_denied", "No new and shiny token for you!"),
|
||||
},
|
||||
{
|
||||
token: token.NewAccessToken("internal-server-error", nil),
|
||||
token: jwt.NewAccessToken("internal-server-error", nil),
|
||||
err: fmt.Errorf("server error: HTTP: %d: %s", http.StatusInternalServerError, "Oh no, it broke"),
|
||||
},
|
||||
} {
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/openid"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
func (h *Handler) Callback(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -41,7 +41,7 @@ func (h *Handler) Callback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
jwkSet := h.Provider.GetPublicJwkSet()
|
||||
|
||||
tokens, err := token.ParseTokens(rawTokens, *jwkSet)
|
||||
tokens, err := jwt.ParseOauth2Token(rawTokens, *jwkSet)
|
||||
if err != nil {
|
||||
h.InternalError(w, r, fmt.Errorf("callback: parsing tokens: %w", err))
|
||||
return
|
||||
@@ -94,7 +94,7 @@ func (h *Handler) codeExchangeForToken(ctx context.Context, loginCookie *openid.
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func logSuccessfulLogin(tokens *token.Tokens, referer string) {
|
||||
func logSuccessfulLogin(tokens *jwt.Tokens, referer string) {
|
||||
fields := log.Fields{
|
||||
"redirect_to": referer,
|
||||
"jti": tokens.JwtIDs(),
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/go-redis/redis/v8"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/session"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
// localSessionID prefixes the given `sid` or `session_state` with the given client ID to prevent key collisions.
|
||||
@@ -64,7 +64,7 @@ func (h *Handler) getSession(ctx context.Context, sessionID string) (*session.Da
|
||||
return sessionData, nil
|
||||
}
|
||||
|
||||
func (h *Handler) getSessionLifetime(accessToken *token.AccessToken) time.Duration {
|
||||
func (h *Handler) getSessionLifetime(accessToken *jwt.AccessToken) time.Duration {
|
||||
defaultSessionLifetime := h.Config.SessionMaxLifetime
|
||||
|
||||
tokenDuration := accessToken.Token.Expiration().Sub(time.Now())
|
||||
@@ -76,7 +76,7 @@ func (h *Handler) getSessionLifetime(accessToken *token.AccessToken) time.Durati
|
||||
return defaultSessionLifetime
|
||||
}
|
||||
|
||||
func (h *Handler) createSession(w http.ResponseWriter, r *http.Request, tokens *token.Tokens, params url.Values) error {
|
||||
func (h *Handler) createSession(w http.ResponseWriter, r *http.Request, tokens *jwt.Tokens, params url.Values) error {
|
||||
externalSessionID, err := NewSessionID(h.Provider.GetOpenIDConfiguration(), tokens.IDToken, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating session ID: %w", err)
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/session"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
func (h *Handler) SessionFallbackExternalIDCookieName() string {
|
||||
@@ -60,7 +60,7 @@ func (h *Handler) GetSessionFallback(r *http.Request) (*session.Data, error) {
|
||||
}
|
||||
|
||||
jwkSet := h.Provider.GetPublicJwkSet()
|
||||
tokens, err := token.ParseTokensFromStrings(idToken, accessToken, *jwkSet)
|
||||
tokens, err := jwt.ParseTokensFromStrings(idToken, accessToken, *jwkSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing tokens: %w", err)
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
jwtlib "github.com/lestrrat-go/jwx/jwt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/mock"
|
||||
"github.com/nais/wonderwall/pkg/router"
|
||||
"github.com/nais/wonderwall/pkg/session"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
func TestHandler_GetSessionFallback(t *testing.T) {
|
||||
@@ -105,7 +105,7 @@ func TestHandler_DeleteSessionFallback(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func makeRequestWithFallbackCookies(t *testing.T, h *router.Handler, tokens *token.Tokens) *http.Request {
|
||||
func makeRequestWithFallbackCookies(t *testing.T, h *router.Handler, tokens *jwt.Tokens) *http.Request {
|
||||
writer := httptest.NewRecorder()
|
||||
expiresIn := time.Minute
|
||||
data := session.NewData("sid", tokens)
|
||||
@@ -150,7 +150,7 @@ func assertCookieExists(t *testing.T, h *router.Handler, cookieName, expectedVal
|
||||
assert.Equal(t, expectedValue, string(plainbytes))
|
||||
}
|
||||
|
||||
func makeTokens(provider mock.TestProvider) *token.Tokens {
|
||||
func makeTokens(provider mock.TestProvider) *jwt.Tokens {
|
||||
jwks := *provider.PrivateJwkSet()
|
||||
|
||||
signer, ok := jwks.Get(0)
|
||||
@@ -158,30 +158,30 @@ func makeTokens(provider mock.TestProvider) *token.Tokens {
|
||||
log.Fatalf("getting signer")
|
||||
}
|
||||
|
||||
idToken := jwt.New()
|
||||
idToken := jwtlib.New()
|
||||
idToken.Set("jti", "id-token-jti")
|
||||
signedIdToken, err := jwt.Sign(idToken, jwa.RS256, signer)
|
||||
signedIdToken, err := jwtlib.Sign(idToken, jwa.RS256, signer)
|
||||
if err != nil {
|
||||
log.Fatalf("signing id_token: %+v", err)
|
||||
}
|
||||
parsedIdToken, err := jwt.Parse(signedIdToken)
|
||||
parsedIdToken, err := jwtlib.Parse(signedIdToken)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing signed id_token: %+v", err)
|
||||
}
|
||||
|
||||
accessToken := jwt.New()
|
||||
accessToken := jwtlib.New()
|
||||
accessToken.Set("jti", "access-token-jti")
|
||||
signedAccessToken, err := jwt.Sign(accessToken, jwa.RS256, signer)
|
||||
signedAccessToken, err := jwtlib.Sign(accessToken, jwa.RS256, signer)
|
||||
if err != nil {
|
||||
log.Fatalf("signing access_token: %+v", err)
|
||||
}
|
||||
parsedAccessToken, err := jwt.Parse(signedAccessToken)
|
||||
parsedAccessToken, err := jwtlib.Parse(signedAccessToken)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing signed access_token: %+v", err)
|
||||
}
|
||||
|
||||
return &token.Tokens{
|
||||
IDToken: token.NewIDToken(string(signedIdToken), parsedIdToken),
|
||||
AccessToken: token.NewAccessToken(string(signedAccessToken), parsedAccessToken),
|
||||
return &jwt.Tokens{
|
||||
IDToken: jwt.NewIDToken(string(signedIdToken), parsedIdToken),
|
||||
AccessToken: jwt.NewAccessToken(string(signedAccessToken), parsedAccessToken),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/openid"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
const (
|
||||
SessionStateParamKey = "session_state"
|
||||
)
|
||||
|
||||
func NewSessionID(cfg *openid.Configuration, idToken *token.IDToken, params url.Values) (string, error) {
|
||||
func NewSessionID(cfg *openid.Configuration, idToken *jwt.IDToken, params url.Values) (string, error) {
|
||||
// 1. check for 'sid' claim in id_token
|
||||
sessionID, err := idToken.GetSidClaim()
|
||||
if err == nil {
|
||||
|
||||
@@ -5,19 +5,19 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
jwtlib "github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/openid"
|
||||
"github.com/nais/wonderwall/pkg/router"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
func TestSessionID(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
config *openid.Configuration
|
||||
idToken *token.IDToken
|
||||
idToken *jwt.IDToken
|
||||
params url.Values
|
||||
want string
|
||||
exactMatch bool
|
||||
@@ -136,8 +136,8 @@ func params(key, value string) url.Values {
|
||||
return values
|
||||
}
|
||||
|
||||
func newIDToken(extraClaims map[string]string) *token.IDToken {
|
||||
idToken := jwt.New()
|
||||
func newIDToken(extraClaims map[string]string) *jwt.IDToken {
|
||||
idToken := jwtlib.New()
|
||||
idToken.Set("sub", "test")
|
||||
idToken.Set("iss", "test")
|
||||
idToken.Set("aud", "test")
|
||||
@@ -150,20 +150,20 @@ func newIDToken(extraClaims map[string]string) *token.IDToken {
|
||||
}
|
||||
}
|
||||
|
||||
serialized, err := jwt.NewSerializer().Serialize(idToken)
|
||||
serialized, err := jwtlib.NewSerializer().Serialize(idToken)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return token.NewIDToken(string(serialized), idToken)
|
||||
return jwt.NewIDToken(string(serialized), idToken)
|
||||
}
|
||||
|
||||
func idTokenWithSid(sid string) *token.IDToken {
|
||||
func idTokenWithSid(sid string) *jwt.IDToken {
|
||||
return newIDToken(map[string]string{
|
||||
"sid": sid,
|
||||
})
|
||||
}
|
||||
|
||||
func idToken() *token.IDToken {
|
||||
func idToken() *jwt.IDToken {
|
||||
return newIDToken(nil)
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
jwtlib "github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/nais/liberator/pkg/keygen"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/crypto"
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/session"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
func TestMemory(t *testing.T) {
|
||||
@@ -19,15 +19,15 @@ func TestMemory(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
crypter := crypto.NewCrypter(key)
|
||||
|
||||
idToken := jwt.New()
|
||||
idToken := jwtlib.New()
|
||||
idToken.Set("jti", "id-token-jti")
|
||||
|
||||
accessToken := jwt.New()
|
||||
accessToken := jwtlib.New()
|
||||
accessToken.Set("jti", "access-token-jti")
|
||||
|
||||
tokens := &token.Tokens{
|
||||
IDToken: token.NewIDToken("id_token", idToken),
|
||||
AccessToken: token.NewAccessToken("access_token", accessToken),
|
||||
tokens := &jwt.Tokens{
|
||||
IDToken: jwt.NewIDToken("id_token", idToken),
|
||||
AccessToken: jwt.NewAccessToken("access_token", accessToken),
|
||||
}
|
||||
data := session.NewData("myid", tokens)
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
jwtlib "github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/nais/liberator/pkg/keygen"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nais/wonderwall/pkg/crypto"
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
"github.com/nais/wonderwall/pkg/session"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
)
|
||||
|
||||
func TestRedis(t *testing.T) {
|
||||
@@ -21,15 +21,15 @@ func TestRedis(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
crypter := crypto.NewCrypter(key)
|
||||
|
||||
idToken := jwt.New()
|
||||
idToken := jwtlib.New()
|
||||
idToken.Set("jti", "id-token-jti")
|
||||
|
||||
accessToken := jwt.New()
|
||||
accessToken := jwtlib.New()
|
||||
accessToken.Set("jti", "access-token-jti")
|
||||
|
||||
tokens := &token.Tokens{
|
||||
IDToken: token.NewIDToken("id_token", idToken),
|
||||
AccessToken: token.NewAccessToken("access_token", accessToken),
|
||||
tokens := &jwt.Tokens{
|
||||
IDToken: jwt.NewIDToken("id_token", idToken),
|
||||
AccessToken: jwt.NewAccessToken("access_token", accessToken),
|
||||
}
|
||||
data := session.NewData("myid", tokens)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/nais/wonderwall/pkg/config"
|
||||
"github.com/nais/wonderwall/pkg/crypto"
|
||||
"github.com/nais/wonderwall/pkg/token"
|
||||
"github.com/nais/wonderwall/pkg/jwt"
|
||||
)
|
||||
|
||||
type Store interface {
|
||||
@@ -80,13 +80,13 @@ func (in *EncryptedData) Decrypt(crypter crypto.Crypter) (*Data, error) {
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
ExternalSessionID string `json:"external_session_id"`
|
||||
AccessToken string `json:"access_token"`
|
||||
IDToken string `json:"id_token"`
|
||||
JwtIDs token.JwtIDs `json:"jti"`
|
||||
ExternalSessionID string `json:"external_session_id"`
|
||||
AccessToken string `json:"access_token"`
|
||||
IDToken string `json:"id_token"`
|
||||
JwtIDs jwt.IDs `json:"jti"`
|
||||
}
|
||||
|
||||
func NewData(externalSessionID string, tokens *token.Tokens) *Data {
|
||||
func NewData(externalSessionID string, tokens *jwt.Tokens) *Data {
|
||||
return &Data{
|
||||
ExternalSessionID: externalSessionID,
|
||||
AccessToken: tokens.AccessToken.Raw,
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Type int
|
||||
|
||||
const (
|
||||
TypeIDToken Type = iota
|
||||
TypeAccessToken
|
||||
)
|
||||
|
||||
const (
|
||||
JtiClaim = "jti"
|
||||
SidClaim = "sid"
|
||||
)
|
||||
|
||||
type Tokens struct {
|
||||
IDToken *IDToken
|
||||
AccessToken *AccessToken
|
||||
}
|
||||
|
||||
func (in *Tokens) JwtIDs() JwtIDs {
|
||||
return JwtIDs{
|
||||
IDToken: in.IDToken.GetJtiClaim(),
|
||||
AccessToken: in.AccessToken.GetJtiClaim(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTokens(idToken *IDToken, accessToken *AccessToken) *Tokens {
|
||||
return &Tokens{
|
||||
IDToken: idToken,
|
||||
AccessToken: accessToken,
|
||||
}
|
||||
}
|
||||
|
||||
type JwtIDs struct {
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
}
|
||||
|
||||
func ParseTokens(tokens *oauth2.Token, jwks jwk.Set) (*Tokens, error) {
|
||||
idToken, ok := tokens.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing id_token in token response")
|
||||
}
|
||||
|
||||
return ParseTokensFromStrings(idToken, tokens.AccessToken, jwks)
|
||||
}
|
||||
|
||||
func ParseTokensFromStrings(idToken, accessToken string, jwks jwk.Set) (*Tokens, error) {
|
||||
parsedIdToken, err := ParseIDToken(idToken, jwks)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("id_token: %w", err)
|
||||
}
|
||||
|
||||
parsedAccessToken, err := ParseAccessToken(accessToken, jwks)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("access_token: %w", err)
|
||||
}
|
||||
|
||||
return NewTokens(parsedIdToken, parsedAccessToken), nil
|
||||
}
|
||||
|
||||
func ParseJwt(raw string, jwks jwk.Set) (jwt.Token, error) {
|
||||
parseOpts := []jwt.ParseOption{
|
||||
jwt.WithKeySet(jwks),
|
||||
jwt.InferAlgorithmFromKey(true),
|
||||
}
|
||||
token, err := jwt.ParseString(raw, parseOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing jwt: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func GetStringClaim(token jwt.Token, claim string) (string, error) {
|
||||
if token == nil {
|
||||
return "", fmt.Errorf("token is nil")
|
||||
}
|
||||
|
||||
gotClaim, ok := token.Get(claim)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("missing required '%s' claim in id_token", claim)
|
||||
}
|
||||
|
||||
claimString, ok := gotClaim.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("'%s' claim is not a string", claim)
|
||||
}
|
||||
|
||||
return claimString, nil
|
||||
}
|
||||
|
||||
func GetStringClaimOrEmpty(token jwt.Token, claim string) string {
|
||||
str, err := GetStringClaim(token, claim)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
Reference in New Issue
Block a user