0

I'm trying to test out PASETO-tokens and I have a problem where I cannot verify my tokens validity properly in the frontend. As I haven't found any browser implementation for checking the signature I've had to do this myself, using https://paseto.io/rfc/ as reference. The token type used is v2 public.

Problem:

The verifier I've written accepts the signature in some cases, but it is dependant on which payload was signed. Would I add another key-value pair to the payload or change the data before generating a new token, the verifier falsely says the signature of the new token is invalid. To look at the implementation, check the sandbox below.

Are there any limits or requirements to the payload that I'm unaware of? Is there an encoding that I'm missing? Or have I just implemented it wrong and do not comply with the PASETO-spec? Any pointers would help.

Replication:

This sandbox showcases my implementation and the issue, with tokens of different payloads, signed by the same ed25519 key: https://codesandbox.io/s/paseto-decoder-lvk7r?file=/paseto.js

It's a svelte repo as that's how I'll use it. But what is the important part is the paseto.js file.

The tokens used in the sandbox were obtained from a backend service for the project, written in Go v1.14:

import (
  "time"
  "encoding/hex"
  "myapplication/model"
  "github.com/o1egl/paseto".    //@v1.0.0
  "golang.org/x/crypto/ed25519" //@v0.0.0-20210421170649-83a5a9bb288b
)

func CreateToken(u *model.U, s *model.S) (string, error) {
  payload := paseto.JSONToken{
    IssuedAt:   time.Now(),
    Expiration: expiresAt,
  }
  footer := "AccessToken"
  
  // All following values are of type string 
  payload.Set("UserID", u.ID)
  payload.Set("Name", u.Name)
  payload.Set("Email", u.Email)
  payload.Set("SpotifyToken", s.AccessToken)

  // Secret from .env in my app, this private key is only for this question
  b, _ := hex.DecodeString("28451edefa6cb29ef7282fc65739b6dd29bb9c2c7ec4166d725252731f102e5c57ff822de765e6f5c566961be40b41f91726d958af2b53ca8dfce85d7d0c1940")
  privateKey := ed25519.PrivateKey(b)

  return paseto.NewV2().Sign(privateKey, payload, footer)
}

Running the paseto.NewV2().Verify() function on the same three tokens in the backend results in all being valid, so there's a difference between the implementation of the Go packages´ verification method and my own JavaScript one.

At this point, I'm not sure where things are going wrong as there are too few implementations to run a check against. As I see it, it could be either my verification method in JS, the Go-package/implementation I'm using to create the tokens, or both. Any pointers to where I'm messing up with different encodings or what is wrong would be very appreciated at this point.

PS: Any secrets (e.g. tokens/keys) in this post and sandbox have been invalidated in my project.

1 Answers1

0

I believe your issue here could be related to the base64 decoding used in the JS example.

I have a similar issue that lead me here, but seems different. However, I do have it noted in my code that the Paseto spec called for the URLSAFE / NOPADDING base64 variant.

I see you are using the Base64-JS lib and i'm not sure what variant of Base64 it will decode by default, but this might explain why you have some valid and false when adding or removing keys to the payload data.

I was using libsodium wrappers in my front end token verify processes, here was some relevant token parsing parts I found for my example...

        // sodium = require('libsodium-wrappers-sumo');
        let dec = new TextDecoder();
        let segments = signed_token.split('.');
        let f = segments.length == 4 ? dec.decode(sodium.from_base64(segments[3], sodium.base64_variants.URLSAFE_NO_PADDING)) : '';
        let msig = sodium.from_base64(segments[2], sodium.base64_variants.URLSAFE_NO_PADDING);

A fresh search on this again turned up this repo https://github.com/jedisct1/js-base64-ct which might seem relevant here, for you as well.