Skip to content

Commit d4e8729

Browse files
authored
[verify] Also allow to obtain the certificate (#39)
Replit identity tokens are really cool because they validate your identity, but they also come equipped with a certificate (a "signing authority" in the code). This code exposes the certificate chain as well as exposing the actual identity.
1 parent bfa9911 commit d4e8729

File tree

1 file changed

+21
-10
lines changed

1 file changed

+21
-10
lines changed

verify.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -372,25 +372,36 @@ type VerifyTokenOpts struct {
372372
//
373373
// The optional options allow specifying additional verifications on the identity.
374374
func VerifyToken(opts VerifyTokenOpts) (*api.GovalReplIdentity, error) {
375+
identity, _, err := VerifyTokenWithCertificate(opts)
376+
return identity, err
377+
}
378+
379+
// VerifyTokenWithCertificate verifies that the given `REPL_IDENTITY` value is
380+
// in fact signed by Goval's chain of authority, and addressed to the provided
381+
// audience (the `REPL_ID` of the recipient). Returns the identity and the
382+
// signing authority (a.k.a. the certificate).
383+
//
384+
// The options allow specifying additional verifications on the identity.
385+
func VerifyTokenWithCertificate(opts VerifyTokenOpts) (*api.GovalReplIdentity, *api.GovalSigningAuthority, error) {
375386
v, bytes, _, err := verifyChain(opts.Message, opts.GetPubKey)
376387
if err != nil {
377-
return nil, fmt.Errorf("failed verify message: %w", err)
388+
return nil, nil, fmt.Errorf("failed verify message: %w", err)
378389
}
379390

380391
signingAuthority, err := getSigningAuthority(opts.Message)
381392
if err != nil {
382-
return nil, fmt.Errorf("failed to read body type: %w", err)
393+
return nil, nil, fmt.Errorf("failed to read body type: %w", err)
383394
}
384395

385396
var identity api.GovalReplIdentity
386397

387398
switch signingAuthority.GetVersion() {
388399
case api.TokenVersion_BARE_REPL_TOKEN:
389-
return nil, errors.New("wrong type of token provided")
400+
return nil, nil, errors.New("wrong type of token provided")
390401
case api.TokenVersion_TYPE_AWARE_TOKEN:
391402
err = proto.Unmarshal(bytes, &identity)
392403
if err != nil {
393-
return nil, fmt.Errorf("failed to decode body: %w", err)
404+
return nil, nil, fmt.Errorf("failed to decode body: %w", err)
394405
}
395406
}
396407

@@ -402,32 +413,32 @@ func VerifyToken(opts VerifyTokenOpts) (*api.GovalReplIdentity, error) {
402413
}
403414
}
404415
if !validAudience {
405-
return nil, fmt.Errorf("message identity mismatch. expected %q, got %q", opts.Audience, identity.Aud)
416+
return nil, nil, fmt.Errorf("message identity mismatch. expected %q, got %q", opts.Audience, identity.Aud)
406417
}
407418

408419
err = v.checkClaimsAgainstToken(&identity)
409420
if err != nil {
410-
return nil, fmt.Errorf("claim mismatch: %w", err)
421+
return nil, nil, fmt.Errorf("claim mismatch: %w", err)
411422
}
412423

413424
if v.claims != nil {
414425
for _, flag := range opts.Flags {
415426
if _, ok := v.claims.Flags[flag]; !ok {
416-
return nil, fmt.Errorf("token not authorized for flag %s", flag)
427+
return nil, nil, fmt.Errorf("token not authorized for flag %s", flag)
417428
}
418429
}
419430
} else if len(opts.Flags) > 0 {
420-
return nil, fmt.Errorf("token not authorized for flags")
431+
return nil, nil, fmt.Errorf("token not authorized for flags")
421432
}
422433

423434
for _, option := range opts.Options {
424435
err = option.verify(&identity)
425436
if err != nil {
426-
return nil, err
437+
return nil, nil, err
427438
}
428439
}
429440

430-
return &identity, nil
441+
return &identity, signingAuthority, nil
431442
}
432443

433444
type verifyRawClaimsOpts struct {

0 commit comments

Comments
 (0)