Files
at-container-registry/pkg/auth/appview_token.go

78 lines
2.2 KiB
Go

package auth
import (
"crypto/ecdh"
"crypto/ecdsa"
"crypto/x509"
"fmt"
"time"
"github.com/bluesky-social/indigo/atproto/atcrypto"
"github.com/golang-jwt/jwt/v5"
)
// CreateAppviewServiceToken creates a short-lived ES256 JWT for appview→hold communication.
// The token authenticates the appview when calling hold XRPC endpoints like updateCrewTier.
//
// Claims:
// - iss: appview DID (e.g. did:web:atcr.io)
// - aud: hold DID (e.g. did:web:hold01.atcr.io)
// - sub: user DID being acted upon
// - exp: now + 60s
// - iat: now
func CreateAppviewServiceToken(privateKey *atcrypto.PrivateKeyP256, appviewDID, holdDID, userDID string) (string, error) {
now := time.Now()
claims := jwt.RegisteredClaims{
Issuer: appviewDID,
Audience: jwt.ClaimStrings{holdDID},
Subject: userDID,
ExpiresAt: jwt.NewNumericDate(now.Add(60 * time.Second)),
IssuedAt: jwt.NewNumericDate(now),
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
ecKey, err := P256ToECDSA(privateKey)
if err != nil {
return "", fmt.Errorf("failed to extract ECDSA key: %w", err)
}
signed, err := token.SignedString(ecKey)
if err != nil {
return "", fmt.Errorf("failed to sign token: %w", err)
}
return signed, nil
}
// P256ToECDSA converts an atcrypto P-256 private key to a stdlib *ecdsa.PrivateKey.
// This is needed because golang-jwt requires stdlib crypto types, while atcrypto
// wraps them in its own types. We re-parse via PKCS8 encoding round-trip.
func P256ToECDSA(key *atcrypto.PrivateKeyP256) (*ecdsa.PrivateKey, error) {
rawBytes := key.Bytes() // 32-byte raw scalar
// Parse raw bytes as ecdh key, then convert via PKCS8 round-trip (same as atcrypto does)
ecdhKey, err := ecdh.P256().NewPrivateKey(rawBytes)
if err != nil {
return nil, fmt.Errorf("failed to parse P-256 raw bytes: %w", err)
}
pkcs8, err := x509.MarshalPKCS8PrivateKey(ecdhKey)
if err != nil {
return nil, fmt.Errorf("failed to marshal PKCS8: %w", err)
}
parsed, err := x509.ParsePKCS8PrivateKey(pkcs8)
if err != nil {
return nil, fmt.Errorf("failed to parse PKCS8: %w", err)
}
ecdsaKey, ok := parsed.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("parsed key is not ECDSA")
}
return ecdsaKey, nil
}