navidrome/core/auth/auth.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

115 lines
2.7 KiB
Go
Raw Normal View History

2020-02-06 22:48:35 +01:00
package auth
import (
"context"
2020-02-06 22:48:35 +01:00
"sync"
"time"
2021-05-11 23:21:18 +02:00
"github.com/go-chi/jwtauth/v5"
2023-12-12 01:32:03 +01:00
"github.com/google/uuid"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/navidrome/navidrome/conf"
2020-02-06 22:48:35 +01:00
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
2020-02-06 22:48:35 +01:00
)
var (
2021-05-12 00:55:58 +02:00
once sync.Once
Secret []byte
TokenAuth *jwtauth.JWTAuth
2020-02-06 22:48:35 +01:00
)
2021-05-12 00:55:58 +02:00
func Init(ds model.DataStore) {
2020-02-06 22:48:35 +01:00
once.Do(func() {
2021-05-12 00:55:58 +02:00
log.Info("Setting Session Timeout", "value", conf.Server.SessionTimeout)
2023-12-12 01:32:03 +01:00
secret, err := ds.Property(context.TODO()).Get(consts.JWTSecretKey)
if err != nil || secret == "" {
2020-02-06 22:48:35 +01:00
log.Error("No JWT secret found in DB. Setting a temp one, but please report this error", err)
2023-12-12 01:32:03 +01:00
secret = uuid.NewString()
2020-02-06 22:48:35 +01:00
}
2021-04-30 16:00:03 +02:00
Secret = []byte(secret)
TokenAuth = jwtauth.New("HS256", Secret, nil)
2020-02-06 22:48:35 +01:00
})
}
2022-12-31 04:34:00 +01:00
func createBaseClaims() map[string]any {
tokenClaims := map[string]any{}
tokenClaims[jwt.IssuerKey] = consts.JWTIssuer
return tokenClaims
}
func CreatePublicToken(claims map[string]any) (string, error) {
tokenClaims := createBaseClaims()
for k, v := range claims {
tokenClaims[k] = v
}
_, token, err := TokenAuth.Encode(tokenClaims)
return token, err
}
2023-01-20 04:52:55 +01:00
func CreateExpiringPublicToken(exp time.Time, claims map[string]any) (string, error) {
tokenClaims := createBaseClaims()
if !exp.IsZero() {
tokenClaims[jwt.ExpirationKey] = exp.UTC().Unix()
}
for k, v := range claims {
tokenClaims[k] = v
}
_, token, err := TokenAuth.Encode(tokenClaims)
return token, err
}
2020-02-06 22:48:35 +01:00
func CreateToken(u *model.User) (string, error) {
2022-12-31 04:34:00 +01:00
claims := createBaseClaims()
2021-05-11 23:21:18 +02:00
claims[jwt.SubjectKey] = u.UserName
claims[jwt.IssuedAtKey] = time.Now().UTC().Unix()
claims["uid"] = u.ID
2020-02-06 22:48:35 +01:00
claims["adm"] = u.IsAdmin
2021-05-11 23:21:18 +02:00
token, _, err := TokenAuth.Encode(claims)
if err != nil {
return "", err
}
2020-02-06 22:48:35 +01:00
return TouchToken(token)
}
2021-05-11 23:21:18 +02:00
func TouchToken(token jwt.Token) (string, error) {
claims, err := token.AsMap(context.Background())
if err != nil {
return "", err
2021-04-30 16:00:03 +02:00
}
2020-02-06 22:48:35 +01:00
2021-05-12 00:55:58 +02:00
claims[jwt.ExpirationKey] = time.Now().UTC().Add(conf.Server.SessionTimeout).Unix()
2021-05-11 23:21:18 +02:00
_, newToken, err := TokenAuth.Encode(claims)
return newToken, err
2021-04-30 16:00:03 +02:00
}
2021-05-11 23:21:18 +02:00
func Validate(tokenStr string) (map[string]interface{}, error) {
token, err := jwtauth.VerifyToken(TokenAuth, tokenStr)
2020-02-06 22:48:35 +01:00
if err != nil {
return nil, err
}
2021-05-11 23:21:18 +02:00
return token.AsMap(context.Background())
2020-02-06 22:48:35 +01:00
}
func WithAdminUser(ctx context.Context, ds model.DataStore) context.Context {
u, err := ds.User(ctx).FindFirstAdmin()
if err != nil {
c, err := ds.User(ctx).CountAll()
if c == 0 && err == nil {
log.Debug(ctx, "Scanner: No admin user yet!", err)
} else {
log.Error(ctx, "Scanner: No admin user found!", err)
}
u = &model.User{}
}
ctx = request.WithUsername(ctx, u.UserName)
return request.WithUser(ctx, *u)
}