This repository has been archived on 2023-11-13. You can view files and clone it, but cannot push or open issues or pull requests.
imagini/internal/auth/auth.go

179 lines
5.1 KiB
Go
Raw Normal View History

2021-01-10 00:44:02 +00:00
package auth
import (
2021-01-18 21:24:28 +00:00
"fmt"
"time"
"errors"
"encoding/json"
2021-01-18 04:56:56 +00:00
2021-01-18 21:24:28 +00:00
"gorm.io/gorm"
2021-01-18 04:56:56 +00:00
"github.com/google/uuid"
2021-01-18 21:24:28 +00:00
log "github.com/sirupsen/logrus"
"github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jwt"
"reichard.io/imagini/internal/db"
"reichard.io/imagini/internal/config"
"reichard.io/imagini/internal/models"
"reichard.io/imagini/internal/session"
2021-01-10 00:44:02 +00:00
)
2021-01-16 22:00:17 +00:00
type AuthManager struct {
2021-01-18 04:56:56 +00:00
DB *db.DBManager
Config *config.Config
Session *session.SessionManager
2021-01-16 22:00:17 +00:00
}
2021-01-18 04:56:56 +00:00
func NewMgr(db *db.DBManager, c *config.Config) *AuthManager {
session := session.NewMgr()
2021-01-16 22:00:17 +00:00
return &AuthManager{
DB: db,
2021-01-18 04:56:56 +00:00
Config: c,
Session: session,
2021-01-16 22:00:17 +00:00
}
}
2021-01-18 21:16:52 +00:00
func (auth *AuthManager) AuthenticateUser(creds models.APICredentials) (bool, models.User) {
2021-01-10 00:44:02 +00:00
// By Username
2021-01-16 22:00:17 +00:00
foundUser, err := auth.DB.User(models.User{Username: creds.User})
2021-01-10 00:44:02 +00:00
if errors.Is(err, gorm.ErrRecordNotFound) {
2021-01-16 22:00:17 +00:00
foundUser, err = auth.DB.User(models.User{Email: creds.User})
2021-01-10 00:44:02 +00:00
}
// Error Checking
if errors.Is(err, gorm.ErrRecordNotFound) {
2021-01-12 04:48:32 +00:00
log.Warn("[auth] User not found: ", creds.User)
2021-01-18 21:16:52 +00:00
return false, foundUser
2021-01-10 00:44:02 +00:00
} else if err != nil {
log.Error(err)
2021-01-18 21:16:52 +00:00
return false, foundUser
2021-01-10 00:44:02 +00:00
}
log.Info("[auth] Authenticating user: ", foundUser.Username)
// Determine Type
switch foundUser.AuthType {
case "Local":
2021-01-18 21:16:52 +00:00
return authenticateLocalUser(foundUser, creds.Password), foundUser
2021-01-10 00:44:02 +00:00
case "LDAP":
2021-01-18 21:16:52 +00:00
return authenticateLDAPUser(foundUser, creds.Password), foundUser
2021-01-10 00:44:02 +00:00
default:
2021-01-18 21:16:52 +00:00
return false, foundUser
2021-01-10 00:44:02 +00:00
}
}
2021-01-18 04:56:56 +00:00
2021-01-18 21:16:52 +00:00
func (auth *AuthManager) getRole(user models.User) string {
// TODO: Lookup role of user
return "User"
}
2021-01-18 04:56:56 +00:00
2021-01-18 21:16:52 +00:00
func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token, bool) {
byteRefreshJWT := []byte(refreshJWT)
2021-01-18 04:56:56 +00:00
2021-01-18 21:16:52 +00:00
// Acquire Relevant Device
unverifiedToken, err := jwt.ParseBytes(byteRefreshJWT)
did, ok := unverifiedToken.Get("did")
if !ok {
return nil, false
}
deviceID, err := uuid.Parse(fmt.Sprintf("%v", did))
if err != nil {
return nil, false
}
device, err := auth.DB.Device(models.Device{Base: models.Base{UUID: deviceID}})
if err != nil {
return nil, false
2021-01-18 04:56:56 +00:00
}
2021-01-19 20:50:48 +00:00
// Verify & Validate Token
verifiedToken, err := jwt.ParseBytes(byteRefreshJWT,
jwt.WithValidate(true),
jwt.WithVerify(jwa.HS256, []byte(device.RefreshKey)),
)
2021-01-18 21:16:52 +00:00
if err != nil {
fmt.Println("failed to parse payload: ", err)
return nil, false
2021-01-18 04:56:56 +00:00
}
2021-01-18 21:16:52 +00:00
return verifiedToken, true
2021-01-18 04:56:56 +00:00
}
2021-01-19 20:50:48 +00:00
func (auth *AuthManager) ValidateJWTAccessToken(accessJWT string) (jwt.Token, bool) {
byteAccessJWT := []byte(accessJWT)
verifiedToken, err := jwt.ParseBytes(byteAccessJWT,
jwt.WithValidate(true),
jwt.WithVerify(jwa.HS256, []byte(auth.Config.JWTSecret)),
)
if err != nil {
fmt.Println("failed to parse payload: ", err)
return nil, false
}
return verifiedToken, true
2021-01-18 04:56:56 +00:00
}
2021-01-18 21:16:52 +00:00
func (auth *AuthManager) CreateJWTRefreshToken(user models.User, device models.Device) (string, error) {
// Acquire Refresh Key
byteKey := []byte(device.RefreshKey)
2021-01-18 04:56:56 +00:00
2021-01-18 21:16:52 +00:00
// Create New Token
tm := time.Now()
t := jwt.New()
2021-01-19 20:50:48 +00:00
t.Set(`did`, device.UUID.String()) // Device ID
t.Set(jwt.SubjectKey, user.UUID.String()) // User ID
t.Set(jwt.AudienceKey, `imagini`) // App ID
t.Set(jwt.IssuedAtKey, tm) // Issued At
// iOS & Android = Never Expiring Refresh Token
if device.Type != "iOS" && device.Type != "Android" {
t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 24)) // 1 Day Access Key
}
2021-01-18 21:16:52 +00:00
// Validate Token Creation
_, err := json.MarshalIndent(t, "", " ")
2021-01-18 04:56:56 +00:00
if err != nil {
2021-01-18 21:16:52 +00:00
fmt.Printf("failed to generate JSON: %s\n", err)
2021-01-18 04:56:56 +00:00
return "", err
}
2021-01-18 21:16:52 +00:00
// Sign Token
signed, err := jwt.Sign(t, jwa.HS256, byteKey)
if err != nil {
log.Printf("failed to sign token: %s", err)
return "", err
}
// Return Token
return string(signed), nil
2021-01-18 04:56:56 +00:00
}
2021-01-18 21:16:52 +00:00
func (auth *AuthManager) CreateJWTAccessToken(user models.User, device models.Device) (string, error) {
2021-01-18 04:56:56 +00:00
// Create New Token
tm := time.Now()
t := jwt.New()
2021-01-19 20:50:48 +00:00
t.Set(`did`, device.UUID.String()) // Device ID
t.Set(`role`, auth.getRole(user)) // User Role (Admin / User)
t.Set(jwt.SubjectKey, user.UUID.String()) // User ID
2021-01-18 04:56:56 +00:00
t.Set(jwt.AudienceKey, `imagini`) // App ID
t.Set(jwt.IssuedAtKey, tm) // Issued At
2021-01-19 20:50:48 +00:00
t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 2)) // 2 Hour Access Key
2021-01-18 04:56:56 +00:00
// Validate Token Creation
_, err := json.MarshalIndent(t, "", " ")
if err != nil {
fmt.Printf("failed to generate JSON: %s\n", err)
return "", err
}
// Use Server Key
byteKey := []byte(auth.Config.JWTSecret)
// Sign Token
signed, err := jwt.Sign(t, jwa.HS256, byteKey)
if err != nil {
log.Printf("failed to sign token: %s", err)
return "", err
}
// Return Token
return string(signed), nil
}