website/pkg/encryption/jwt.go
Noah Petherbridge 7869ff83ba Signed and Authenticated Static Photo URLs
* Add support for authenticated static photo URLs, leveraging the NGINX module
  ngx_http_auth_request. The README is updated with an example NGINX config
  how to set this up on the proxy side.
* In settings.json a new SignedPhoto section is added: not enabled by default.
* PhotoURL will append a ?jwt= token to the /static/photos/ path for the
  current user, which expires after 30 seconds.
* When SignedPhoto is enabled, it will enforce that the JWT token is valid and
  matches the username of the current logged-in user, or else will return with
  a 403 Forbidden error.
2024-10-03 18:04:14 -07:00

72 lines
1.7 KiB
Go

package encryption
import (
"errors"
"fmt"
"time"
"code.nonshy.com/nonshy/website/pkg/config"
"github.com/golang-jwt/jwt/v4"
)
// StandardClaims returns a standard JWT claim for a username.
//
// It will include values for Subject (username), Issuer (site title), ExpiresAt, IssuedAt, NotBefore.
//
// If the userID is >0, the ID field is included.
func StandardClaims(userID uint64, username string, expires time.Duration) jwt.RegisteredClaims {
claim := jwt.RegisteredClaims{
Subject: username,
Issuer: config.Title,
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expires)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
}
if userID > 0 {
claim.ID = fmt.Sprintf("%d", userID)
}
return claim
}
// SignClaims creates and returns a signed JWT token.
func SignClaims(claims jwt.Claims, secret []byte) (string, error) {
// Get our Chat JWT secret.
if len(secret) == 0 {
return "", errors.New("JWT secret key is not configured")
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(secret)
if err != nil {
return "", err
}
return ss, nil
}
// ValidateClaims checks a JWT token is signed by the site key and returns the claims.
func ValidateClaims(tokenStr string, secret []byte, v jwt.Claims) (jwt.Claims, bool, error) {
// Handle a JWT authentication token.
var (
claims jwt.Claims
authOK bool
)
if tokenStr != "" {
token, err := jwt.ParseWithClaims(tokenStr, v, func(token *jwt.Token) (interface{}, error) {
return secret, nil
})
if err != nil {
return nil, false, err
}
if !token.Valid {
return nil, false, errors.New("token was not valid")
}
claims = token.Claims
authOK = true
}
return claims, authOK, nil
}