···
···
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
slogecho "github.com/samber/slog-echo"
+
"gitlab.com/yawning/secp256k1-voi"
+
secp256k1secec "gitlab.com/yawning/secp256k1-voi/secec"
···
return helpers.ServerError(e, nil)
+
// move on to oauth session middleware if this is a dpop token
+
token, _, err := new(jwt.Parser).ParseUnverified(tokenstr, jwt.MapClaims{})
+
claims, ok := token.Claims.(jwt.MapClaims)
+
return helpers.InputError(e, to.StringPtr("InvalidToken"))
+
var repo *models.RepoActor
+
lxm, hasLxm := claims["lxm"]
+
pts := strings.Split(e.Request().URL.String(), "/")
+
if lxm != pts[len(pts)-1] {
+
s.logger.Error("service auth lxm incorrect", "lxm", lxm, "expected", pts[len(pts)-1], "error", err)
+
return helpers.InputError(e, nil)
+
maybeDid, ok := claims["iss"].(string)
+
s.logger.Error("no iss in service auth token", "error", err)
+
return helpers.InputError(e, nil)
+
maybeRepo, err := s.getRepoActorByDid(did)
+
s.logger.Error("error fetching repo", "error", err)
+
return helpers.ServerError(e, nil)
+
if token.Header["alg"] != "ES256K" {
+
token, err = new(jwt.Parser).Parse(tokenstr, func(t *jwt.Token) (any, error) {
+
if _, ok := t.Method.(*jwt.SigningMethodECDSA); !ok {
+
return nil, fmt.Errorf("unsupported signing method: %v", t.Header["alg"])
+
return s.privateKey.Public(), nil
+
s.logger.Error("error parsing jwt", "error", err)
+
// NOTE: https://github.com/bluesky-social/atproto/discussions/3319
+
return e.JSON(400, map[string]string{"error": "ExpiredToken", "message": "token has expired"})
+
return helpers.InputError(e, to.StringPtr("InvalidToken"))
+
kpts := strings.Split(tokenstr, ".")
+
signingInput := kpts[0] + "." + kpts[1]
+
hash := sha256.Sum256([]byte(signingInput))
+
sigBytes, err := base64.RawURLEncoding.DecodeString(kpts[2])
+
s.logger.Error("error decoding signature bytes", "error", err)
+
return helpers.ServerError(e, nil)
+
if len(sigBytes) != 64 {
+
s.logger.Error("incorrect sigbytes length", "length", len(sigBytes))
+
return helpers.ServerError(e, nil)
+
rBytes := sigBytes[:32]
+
sBytes := sigBytes[32:]
+
rr, _ := secp256k1.NewScalarFromBytes((*[32]byte)(rBytes))
+
ss, _ := secp256k1.NewScalarFromBytes((*[32]byte)(sBytes))
+
sk, err := secp256k1secec.NewPrivateKey(repo.SigningKey)
+
s.logger.Error("can't load private key", "error", err)
+
pubKey, ok := sk.Public().(*secp256k1secec.PublicKey)
+
s.logger.Error("error getting public key from sk")
+
return helpers.ServerError(e, nil)
+
verified := pubKey.VerifyRaw(hash[:], rr, ss)
+
s.logger.Error("error verifying", "error", err)
+
return helpers.ServerError(e, nil)
isRefresh := e.Request().URL.Path == "/xrpc/com.atproto.server.refreshSession"
+
scope, _ := claims["scope"].(string)
if isRefresh && scope != "com.atproto.refresh" {
return helpers.InputError(e, to.StringPtr("InvalidToken"))
+
} else if !hasLxm && !isRefresh && scope != "com.atproto.access" {
return helpers.InputError(e, to.StringPtr("InvalidToken"))
···
+
if err := s.db.Raw("SELECT EXISTS(SELECT 1 FROM "+table+" WHERE token = ?) AS found", nil, tokenstr).Scan(&result).Error; err != nil {
+
if err == gorm.ErrRecordNotFound {
+
return helpers.InputError(e, to.StringPtr("InvalidToken"))
+
s.logger.Error("error getting token from db", "error", err)
+
return helpers.ServerError(e, nil)
+
return helpers.InputError(e, to.StringPtr("InvalidToken"))
exp, ok := claims["exp"].(float64)
···
return helpers.InputError(e, to.StringPtr("ExpiredToken"))
+
maybeRepo, err := s.getRepoActorByDid(claims["sub"].(string))
+
s.logger.Error("error fetching repo", "error", err)
+
return helpers.ServerError(e, nil)
if err := next(e); err != nil {