78 lines
2.2 KiB
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
|
|
}
|