aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2026-03-29 21:05:08 +0700
committerShulhan <ms@kilabit.info>2026-03-29 22:17:14 +0700
commit5335f8d5e049866c904b506e28a7e48c8c787024 (patch)
treeb4a13aa72ce8c354a7822dd949d92f9e8c90ffb1
parenta2245d5be247ef93944c16cbeed7fbb1dc9bab1d (diff)
downloadpakakeh.go-5335f8d5e049866c904b506e28a7e48c8c787024.tar.xz
lib/paseto: move message, payload, footer, and key type from v2
The [v2/PublicToken] has been moved and renamed as [paseto.Message]. The Data field in the Message is removed since its duplicate with [Payload.Data]. The [v2/JSONToken] has been moved and renamed as [paseto.Payload]. In the [Payload.Validate] method, we remove the validation for IssuedAt field, since its usage is to store the time the token is created. The Data field type in the Payload changes from string to any. The [v2/JSONFooter] has been moved and renamed as [paseto.Footer]. The type of Data field in Footer changes from map[string]any to any. The KID field in the Footer has been renamed to PeerID along with its json identifer. The [v2/Key] has been moved and renamed as [paseto.Peer].
-rw-r--r--lib/paseto/footer.go12
-rw-r--r--lib/paseto/message.go10
-rw-r--r--lib/paseto/paseto.go6
-rw-r--r--lib/paseto/payload.go113
-rw-r--r--lib/paseto/payload_test.go44
-rw-r--r--lib/paseto/peer.go25
-rw-r--r--lib/paseto/v2/example_public_mode_test.go12
-rw-r--r--lib/paseto/v2/json_footer.go11
-rw-r--r--lib/paseto/v2/json_token.go83
-rw-r--r--lib/paseto/v2/json_token_test.go49
-rw-r--r--lib/paseto/v2/key.go25
-rw-r--r--lib/paseto/v2/keys.go14
-rw-r--r--lib/paseto/v2/paseto.go51
-rw-r--r--lib/paseto/v2/public_mode.go54
-rw-r--r--lib/paseto/v2/public_mode_test.go5
-rw-r--r--lib/paseto/v2/public_token.go11
16 files changed, 277 insertions, 248 deletions
diff --git a/lib/paseto/footer.go b/lib/paseto/footer.go
new file mode 100644
index 00000000..acaa3714
--- /dev/null
+++ b/lib/paseto/footer.go
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2020 M. Shulhan <ms@kilabit.info>
+
+package paseto
+
+// Footer defines the peer ID and optional data for the message.
+// Footer is not included during signing payload, it can be tampered, so it
+// MUST NOT include sensitive information.
+type Footer struct {
+ Data any `json:"data,omitempty"`
+ PeerID string `json:"peer_id"`
+}
diff --git a/lib/paseto/message.go b/lib/paseto/message.go
new file mode 100644
index 00000000..47718f9e
--- /dev/null
+++ b/lib/paseto/message.go
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
+
+package paseto
+
+// Message defines the payload be signed and verified by sender/receiver.
+type Message struct {
+ Payload Payload
+ Footer Footer
+}
diff --git a/lib/paseto/paseto.go b/lib/paseto/paseto.go
index f903fd79..05865bcb 100644
--- a/lib/paseto/paseto.go
+++ b/lib/paseto/paseto.go
@@ -18,8 +18,14 @@ import (
"bytes"
"encoding/binary"
"fmt"
+ "time"
)
+// DefaultTTL define the time-to-live of a message, by setting ExpiredAt to
+// current time + [DefaultTTL].
+// This value can be changes to increase the message expiration time.
+var DefaultTTL = 60 * time.Second
+
// PreAuthEncode encodes each piece into single block.
func PreAuthEncode(pieces ...[]byte) (b []byte, err error) {
logp := `PreAuthEncode`
diff --git a/lib/paseto/payload.go b/lib/paseto/payload.go
new file mode 100644
index 00000000..2f0de5fe
--- /dev/null
+++ b/lib/paseto/payload.go
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
+
+package paseto
+
+import (
+ "errors"
+ "fmt"
+ "time"
+)
+
+// DriftSeconds defines the time differences in seconds allowed in
+// [Payload.ExpiredAt] and [Payload.NotBefore].
+const DriftSeconds = 5 * time.Second
+
+// List of errors for [Payload.Validate].
+var (
+ ErrUnknownIssuer = errors.New(`unknown issuer`)
+ ErrUnknownSubject = errors.New(`unknown subject`)
+ ErrInvalidAudience = errors.New(`invalid audience`)
+ ErrExpired = errors.New(`expired`)
+ ErrNotBeforeAfter = errors.New(`not-before is after current time`)
+)
+
+// Payload represents the data and claims.
+//
+// The claims follow RFC 7519 that includes issuer, subject, audience,
+// expiration time, not-before time, issued-at, and ID.
+type Payload struct {
+ // Issuer defines the peer ID that issued the payload.
+ Issuer string `json:"iss,omitempty"`
+
+ // Subject defines the scope of payload.
+ Subject string `json:"sub,omitempty"`
+
+ // Audience defines the peer ID that receive the payload.
+ Audience string `json:"aud,omitempty"`
+
+ // ExpiredAt defines the expiration time when the payload MUST NOT
+ // be accepted for processing.
+ ExpiredAt *time.Time `json:"exp,omitempty"`
+
+ // NotBefore defines the time when the payload MUST NOT be accepted
+ // for processing.
+ NotBefore *time.Time `json:"nbf,omitempty"`
+
+ // IssuedAt defines the time at which the payload is issued.
+ IssuedAt *time.Time `json:"iat,omitempty"`
+
+ // TokenID defines the unique identifier for the payload.
+ TokenID string `json:"jti,omitempty"`
+
+ // Data defines actual information to be send in message.
+ // Data must be JSON encodable.
+ Data any `json:"data"`
+}
+
+// Validate validates the the payload and returns nil when all of the
+// following conditions satisfied,
+//
+// - The Issuer field MUST equal to sender [Peer.ID].
+// - The Subject field MUST be in one of sender [Peer.AllowedSubjects] if
+// its is not empty.
+// - The Audience field MUST equal to recvID if its not empty.
+// - The ExpiredAt field MUST before the current time.
+// - The NotBefore field MUST equal or before the current time.
+//
+// The relation between current time, ExpiredAt, and NotBefore can be viewed
+// this way
+//
+// iat/nbf exp
+// -----o-----------------o----- time
+// / \ / \
+// invalid ---+ +--- valid ---+ +--- invalid
+//
+// If one of the above condition is not passed, it will return an error.
+func (pload *Payload) Validate(recvID string, sender Peer) (err error) {
+ logp := `payload`
+
+ now := time.Now().Round(time.Second)
+ if pload.Issuer != sender.ID {
+ return fmt.Errorf(`%s: %w`, logp, ErrUnknownIssuer)
+ }
+ if len(sender.AllowedSubjects) != 0 {
+ _, ok := sender.AllowedSubjects[pload.Subject]
+ if !ok {
+ return fmt.Errorf(`%s: %w`, logp, ErrUnknownSubject)
+ }
+ }
+ if len(recvID) != 0 && pload.Audience != recvID {
+ return fmt.Errorf(`%s: %w`, logp, ErrInvalidAudience)
+ }
+ if pload.ExpiredAt != nil {
+ diff := pload.ExpiredAt.Sub(now)
+ diff -= DefaultTTL
+ if diff < 0 {
+ diff *= -1
+ }
+ if diff > DriftSeconds {
+ return fmt.Errorf(`%s: %w`, logp, ErrExpired)
+ }
+ }
+ if pload.NotBefore != nil {
+ diff := now.Sub(*pload.NotBefore)
+ if diff < 0 {
+ diff *= -1
+ }
+ if diff > DriftSeconds {
+ return fmt.Errorf(`%s: %w`, logp, ErrNotBeforeAfter)
+ }
+ }
+ return nil
+}
diff --git a/lib/paseto/payload_test.go b/lib/paseto/payload_test.go
new file mode 100644
index 00000000..81d1c67f
--- /dev/null
+++ b/lib/paseto/payload_test.go
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
+
+package paseto
+
+import (
+ "testing"
+ "time"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/test"
+)
+
+func TestPayload_Validate(t *testing.T) {
+ now := time.Now().Round(time.Second)
+ peer := Peer{}
+
+ issued1sAgo := now.Add(-1 * time.Second)
+ issued6sAgo := now.Add(-6 * time.Second)
+
+ listCase := []struct {
+ desc string
+ pload *Payload
+ expErr string
+ }{{
+ desc: `With IssuedAt less than current time`,
+ pload: &Payload{
+ IssuedAt: &issued1sAgo,
+ },
+ }, {
+ desc: `With IssuedAt greater than drift`,
+ pload: &Payload{
+ IssuedAt: &issued6sAgo,
+ },
+ expErr: `payload: issued-at is after current time`,
+ }}
+
+ for _, tc := range listCase {
+ err := tc.pload.Validate(``, peer)
+ if err != nil {
+ test.Assert(t, tc.desc, tc.expErr, err.Error())
+ continue
+ }
+ }
+}
diff --git a/lib/paseto/peer.go b/lib/paseto/peer.go
new file mode 100644
index 00000000..8c077bb7
--- /dev/null
+++ b/lib/paseto/peer.go
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
+
+package paseto
+
+import "crypto/ed25519"
+
+// Peer represents the principal in public mode, the one that sends and
+// receives the message.
+type Peer struct {
+ // AllowedSubjects list of scope that are allowed for this peer.
+ // This field is used by receiver to check the claim "sub" and compare
+ // it with this list.
+ // Empty list means allowing all scopes.
+ AllowedSubjects map[string]struct{}
+
+ // ID is a unique identifier of the peer.
+ ID string
+
+ // PrivateKey for signing message.
+ Private ed25519.PrivateKey
+
+ // PublicKey for verifying message.
+ Public ed25519.PublicKey
+}
diff --git a/lib/paseto/v2/example_public_mode_test.go b/lib/paseto/v2/example_public_mode_test.go
index 0930aebf..e91f56a2 100644
--- a/lib/paseto/v2/example_public_mode_test.go
+++ b/lib/paseto/v2/example_public_mode_test.go
@@ -8,6 +8,8 @@ import (
"encoding/hex"
"fmt"
"log"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/paseto"
)
func ExamplePublicMode() {
@@ -15,7 +17,7 @@ func ExamplePublicMode() {
senderSK, _ := hex.DecodeString("e9ae9c7eae2fce6fd6727b5ca8df0fbc0aa60a5ffb354d4fdee1729e4e1463688d2160a4dc71a9a697d6ad6424da3f9dd18a259cdd51b0ae2b521e998b82d36e")
senderPK, _ := hex.DecodeString("8d2160a4dc71a9a697d6ad6424da3f9dd18a259cdd51b0ae2b521e998b82d36e")
- senderKey := Key{
+ senderKey := paseto.Peer{
ID: "sender",
Private: ed25519.PrivateKey(senderSK),
Public: ed25519.PublicKey(senderPK),
@@ -26,7 +28,7 @@ func ExamplePublicMode() {
receiverSK, _ := hex.DecodeString("4983da648bff1fd3e1892df9c56370215aa640829a5cab02d6616b115fa0bc5707c22e74ab9b181f8d87bdf03cf88476ec4c35e5517e173f236592f6695d59f5")
receiverPK, _ := hex.DecodeString("07c22e74ab9b181f8d87bdf03cf88476ec4c35e5517e173f236592f6695d59f5")
- receiverKey := Key{
+ receiverKey := paseto.Peer{
ID: "receiver",
Private: ed25519.PrivateKey(receiverSK),
Public: ed25519.PublicKey(receiverPK),
@@ -81,7 +83,7 @@ func ExamplePublicMode() {
log.Fatal(err)
}
- fmt.Printf("Received data: %s\n", got.Data)
+ fmt.Printf("Received data: %s\n", got.Payload.Data)
fmt.Printf("Received footer: %+v\n", got.Footer)
// receiver receive invalid token from sender and unpack it ...
@@ -92,6 +94,6 @@ func ExamplePublicMode() {
// Output:
// Received data: hello receiver
- // Received footer: {Data:map[FOOTER:HERE] KID:sender}
- // token subject "unknown-subject" is not allowed for key "sender"
+ // Received footer: {Data:map[FOOTER:HERE] PeerID:sender}
+ // payload: unknown subject
}
diff --git a/lib/paseto/v2/json_footer.go b/lib/paseto/v2/json_footer.go
deleted file mode 100644
index 67a97444..00000000
--- a/lib/paseto/v2/json_footer.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020 M. Shulhan <ms@kilabit.info>
-
-package pasetov2
-
-// JSONFooter define the optional metadata and data at the footer of the
-// token that are not included in signature.
-type JSONFooter struct {
- Data map[string]any `json:"data,omitempty"`
- KID string `json:"kid"`
-}
diff --git a/lib/paseto/v2/json_token.go b/lib/paseto/v2/json_token.go
deleted file mode 100644
index a3b6006d..00000000
--- a/lib/paseto/v2/json_token.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
-
-package pasetov2
-
-import (
- "fmt"
- "time"
-)
-
-const (
- _validateTimeDrift = 5 * time.Second
-)
-
-// JSONToken define the metadata and data inside the token that are included
-// to generate the signature.
-type JSONToken struct {
- Issuer string `json:"iss,omitempty"`
- Subject string `json:"sub,omitempty"`
- Audience string `json:"aud,omitempty"`
- ExpiredAt *time.Time `json:"exp,omitempty"`
- NotBefore *time.Time `json:"nbf,omitempty"`
- IssuedAt *time.Time `json:"iat,omitempty"`
- TokenID string `json:"jti,omitempty"`
- Data string `json:"data"`
-}
-
-// Validate the JSON token fields,
-//
-// - The Issuer must equal to peer.ID
-// - The Audience must equal to received ID,
-// - If peer.AllowedSubjects is not empty, the Subject value must be in
-// one of them,
-// - The current time must be after IssuedAt field,
-// - The current time must after NotBefore "nbf" field,
-// - The current time must before ExpiredAt field.
-//
-// If one of the above condition is not passed, it will return an error.
-func (jtoken *JSONToken) Validate(audience string, peer Key) (err error) {
- now := time.Now().Round(time.Second)
- if jtoken.Issuer != peer.ID {
- return fmt.Errorf("expecting issuer %q, got %q", peer.ID,
- jtoken.Issuer)
- }
- if len(peer.AllowedSubjects) != 0 {
- _, isAllowed := peer.AllowedSubjects[jtoken.Subject]
- if !isAllowed {
- return fmt.Errorf("token subject %q is not allowed for key %q",
- jtoken.Subject, peer.ID)
- }
- }
- if len(audience) != 0 {
- if jtoken.Audience != audience {
- return fmt.Errorf("expecting audience %q, got %q",
- audience, jtoken.Audience)
- }
- }
- if jtoken.IssuedAt != nil {
- diff := now.Sub(*jtoken.IssuedAt)
- if diff < 0 {
- diff *= -1
- }
- if diff > _validateTimeDrift {
- return fmt.Errorf("token issued at %s before current time %s",
- jtoken.IssuedAt, now)
- }
- }
- if jtoken.NotBefore != nil {
- diff := now.Sub(*jtoken.NotBefore)
- if diff < 0 {
- diff *= -1
- }
- if diff > _validateTimeDrift {
- return fmt.Errorf("token must not used before %s", jtoken.NotBefore)
- }
- }
- if jtoken.ExpiredAt != nil {
- if now.After(*jtoken.ExpiredAt) {
- return fmt.Errorf("token is expired %s", jtoken.ExpiredAt)
- }
- }
- return nil
-}
diff --git a/lib/paseto/v2/json_token_test.go b/lib/paseto/v2/json_token_test.go
deleted file mode 100644
index 5081ebf4..00000000
--- a/lib/paseto/v2/json_token_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
-
-package pasetov2
-
-import (
- "fmt"
- "testing"
- "time"
-
- "git.sr.ht/~shulhan/pakakeh.go/lib/test"
-)
-
-func TestJSONToken_Validate(t *testing.T) {
- now := time.Now().Round(time.Second)
- peer := Key{}
-
- issued1sAgo := now.Add(-1 * time.Second)
- issued6sAgo := now.Add(-6 * time.Second)
-
- cases := []struct {
- desc string
- jtoken *JSONToken
- expErr string
- }{{
- desc: "With IssuedAt less than current time",
- jtoken: &JSONToken{
- IssuedAt: &issued1sAgo,
- },
- }, {
- desc: "With IssuedAt greater than drift",
- jtoken: &JSONToken{
- IssuedAt: &issued6sAgo,
- },
- expErr: fmt.Sprintf("token issued at %s before current time %s",
- issued6sAgo, now),
- }}
-
- for _, c := range cases {
- var gotErr string
-
- err := c.jtoken.Validate("", peer)
- if err != nil {
- gotErr = err.Error()
- }
-
- test.Assert(t, c.desc, c.expErr, gotErr)
- }
-}
diff --git a/lib/paseto/v2/key.go b/lib/paseto/v2/key.go
deleted file mode 100644
index fdd51988..00000000
--- a/lib/paseto/v2/key.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
-
-package pasetov2
-
-import "crypto/ed25519"
-
-// Key define private and public key for public mode.
-type Key struct {
- // AllowedSubjects contains list of subject that are allowed in the
- // token's claim "sub" to be signed by this public key.
- // This field is used by receiver to check the claim "sub" and compare
- // it with this list.
- // Empty list means allowing all subjects.
- AllowedSubjects map[string]struct{}
-
- // ID is a unique key ID.
- ID string
-
- // PrivateKey for signing public token.
- Private ed25519.PrivateKey
-
- // PublicKey for verifying public token.
- Public ed25519.PublicKey
-}
diff --git a/lib/paseto/v2/keys.go b/lib/paseto/v2/keys.go
index bfe7e97f..5095e82f 100644
--- a/lib/paseto/v2/keys.go
+++ b/lib/paseto/v2/keys.go
@@ -3,28 +3,32 @@
package pasetov2
-import "sync"
+import (
+ "sync"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/paseto"
+)
// keys contains and maintains list of public Keys and its configuration.
type keys struct {
- v map[string]Key
+ v map[string]paseto.Peer
sync.Mutex
}
func newKeys() *keys {
return &keys{
- v: make(map[string]Key),
+ v: make(map[string]paseto.Peer),
}
}
-func (p *keys) upsert(k Key) {
+func (p *keys) upsert(k paseto.Peer) {
p.Lock()
p.v[k.ID] = k
p.Unlock()
}
-func (p *keys) get(id string) (k Key, ok bool) {
+func (p *keys) get(id string) (k paseto.Peer, ok bool) {
p.Lock()
k, ok = p.v[id]
p.Unlock()
diff --git a/lib/paseto/v2/paseto.go b/lib/paseto/v2/paseto.go
index a7fb7f77..c9b903f4 100644
--- a/lib/paseto/v2/paseto.go
+++ b/lib/paseto/v2/paseto.go
@@ -21,7 +21,10 @@
// The public mode focus on signing and verifing data, everything else is
// handled and filled automatically.
//
-// Steps for sender when generating new token, the Pack() method,
+// === Sender side
+//
+// Steps for generating new token, the [PublicMode.Pack] method, on sender
+// side:
//
// (1) Prepare the JSON token claims, set
//
@@ -35,17 +38,17 @@
//
// (2) Prepare the JSON footer, set
//
-// - Key ID "kid" to PublicMode.our.ID
+// - Peer ID "peer_id" to PublicMode.our.ID
//
// The user's claims data is stored using key "data" inside the JSON token,
// encoded using base64 (with padding).
// Additional footer data can be added on the Data field.
//
-// Overall, the following JSONToken and JSONFooter is generated for each
-// token,
+// Overall, the following [paseto.Payload] and [paseto.Footer] is generated
+// for each token,
//
-// JSONToken:{
-// "iss": <Key.ID>,
+// Payload:{
+// "iss": <Peer.ID>,
// "sub": <Subject parameter>,
// "aud": <Audience parameter>
// "exp": <time.Now() + TTL>,
@@ -53,16 +56,18 @@
// "nbf": <time.Now()>,
// "data": <base64.StdEncoding.EncodeToString(userData)>,
// }
-// JSONFooter:{
-// "kid": <Key.ID>,
+// Footer:{
+// "peer_id": <Peer.ID>,
// "data": {}
// }
//
-// On the receiver side, they will have list of registered peers Key (include
-// ID, public Key, and list of allowed subject).
+// === Receiver side
+//
+// On the receiver side, they will have list of registered peers (including
+// their ID, public key, and list of allowed subject).
//
// PublicMode:{
-// peers: map[Key.ID]Key{
+// peers: map[Peer.ID]Peer{
// Public: <ed25519.PublicKey>,
// AllowedSubjects: map[string]struct{}{
// "/api/x": struct{}{},
@@ -73,29 +78,17 @@
// },
// }
//
-// Step for receiver to process the token, the Unpack() method,
+// Step for receiver to process the token, the [PublicMode.Unpack] method,
//
-// (1) Decode the token footer
+// (1) Decode the token footer.
//
-// (2) Get the registered public key based on "kid" value in token footer.
-// If no peers key exist matched with "kid" value, reject the token.
+// (2) Get the registered public key based on "peer_id" value in token footer.
+// If no peers key exist matched with "peer_id" value, reject the token.
//
-// (3) Verify the token using the peer public key.
+// (3) Verifies the token using the peer public key.
// If verification failed, reject the token.
//
-// (4) Validate the token.
-// - The Issuer must equal to peer ID.
-// - The Audience must equal to receiver ID.
-// - If the peer AllowedSubjects is not empty, the Subject must be in
-// one of them.
-// - The current time must be after IssuedAt.
-// - The current time must be after NotBefore.
-// - The current time must be before ExpiredAt.
-// - If one of the above condition is not passed, it will return an error.
-//
-// # References
-//
-// - [paseto-rfc-01]
+// (4) Validates the token.
//
// [paseto-rfc-01]: https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Version2.md
package pasetov2
diff --git a/lib/paseto/v2/public_mode.go b/lib/paseto/v2/public_mode.go
index 98d7d2da..d322a0b1 100644
--- a/lib/paseto/v2/public_mode.go
+++ b/lib/paseto/v2/public_mode.go
@@ -12,6 +12,8 @@ import (
"net/http"
"strings"
"time"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/paseto"
)
const (
@@ -20,23 +22,18 @@ const (
keyBearer = "bearer"
)
-// DefaultTTL define the time-to-live of each token, by setting ExpiredAt to
-// current time + DefaultTTL.
-// If you want longer token, increase this value before using Pack().
-var DefaultTTL = 60 * time.Second
-
// PublicMode implement the PASETO public mode to signing and verifying data
// using private key and one or more shared public keys.
// The PublicMode contains list of peer public keys for verifying the incoming
// token.
type PublicMode struct {
peers *keys
- our Key
+ our paseto.Peer
}
// NewPublicMode create new PublicMode with our private key for signing
// outgoing token.
-func NewPublicMode(our Key) (auth *PublicMode, err error) {
+func NewPublicMode(our paseto.Peer) (auth *PublicMode, err error) {
if len(our.ID) == 0 {
return nil, errors.New(`empty key ID`)
}
@@ -53,7 +50,7 @@ func NewPublicMode(our Key) (auth *PublicMode, err error) {
// UnpackHTTPRequest unpack token from HTTP request header "Authorization" or
// from query parameter "access_token".
func (auth *PublicMode) UnpackHTTPRequest(req *http.Request) (
- publicToken *PublicToken, err error,
+ msg *paseto.Message, err error,
) {
if req == nil {
return nil, errors.New(`empty HTTP request`)
@@ -82,9 +79,9 @@ func (auth *PublicMode) UnpackHTTPRequest(req *http.Request) (
return auth.Unpack(token)
}
-// AddPeer add a key to list of known peers for verifying incoming token.
-// The only required fields in Key is ID and Public.
-func (auth *PublicMode) AddPeer(k Key) (err error) {
+// AddPeer add a peer to list of known peers for verifying incoming token.
+// The only required fields in [paseto.Peer] is ID and Public.
+func (auth *PublicMode) AddPeer(k paseto.Peer) (err error) {
if len(k.ID) == 0 {
return errors.New(`empty key ID`)
}
@@ -96,7 +93,7 @@ func (auth *PublicMode) AddPeer(k Key) (err error) {
}
// GetPeerKey get the peer's key based on key ID.
-func (auth *PublicMode) GetPeerKey(id string) (k Key, ok bool) {
+func (auth *PublicMode) GetPeerKey(id string) (k paseto.Peer, ok bool) {
return auth.peers.get(id)
}
@@ -110,8 +107,8 @@ func (auth *PublicMode) Pack(audience, subject string, data []byte, footer map[s
token string, err error,
) {
now := time.Now().Round(time.Second)
- expiredAt := now.Add(DefaultTTL)
- jsonToken := JSONToken{
+ expiredAt := now.Add(paseto.DefaultTTL)
+ jsonToken := paseto.Payload{
Issuer: auth.our.ID,
Subject: subject,
Audience: audience,
@@ -126,9 +123,9 @@ func (auth *PublicMode) Pack(audience, subject string, data []byte, footer map[s
return "", err
}
- jsonFooter := JSONFooter{
- KID: auth.our.ID,
- Data: footer,
+ jsonFooter := paseto.Footer{
+ PeerID: auth.our.ID,
+ Data: footer,
}
rawfooter, err := json.Marshal(&jsonFooter)
@@ -139,8 +136,8 @@ func (auth *PublicMode) Pack(audience, subject string, data []byte, footer map[s
return Sign(auth.our.Private, msg, rawfooter)
}
-// Unpack the token to get the JSONToken and the data.
-func (auth *PublicMode) Unpack(token string) (publicToken *PublicToken, err error) {
+// Unpack the token to get the Payload and the data.
+func (auth *PublicMode) Unpack(token string) (msg *paseto.Message, err error) {
pieces := strings.Split(token, ".")
if len(pieces) != 4 {
return nil, errors.New(`invalid token format`)
@@ -152,21 +149,21 @@ func (auth *PublicMode) Unpack(token string) (publicToken *PublicToken, err erro
return nil, errors.New(`expecting public mode, got ` + pieces[1])
}
- publicToken = &PublicToken{}
+ msg = &paseto.Message{}
rawfooter, err := base64.RawURLEncoding.DecodeString(pieces[3])
if err != nil {
return nil, err
}
- err = json.Unmarshal(rawfooter, &publicToken.Footer)
+ err = json.Unmarshal(rawfooter, &msg.Footer)
if err != nil {
return nil, err
}
- peerKey, ok := auth.peers.get(publicToken.Footer.KID)
+ peerKey, ok := auth.peers.get(msg.Footer.PeerID)
if !ok {
return nil, fmt.Errorf("unknown peer key ID %s",
- publicToken.Footer.KID)
+ msg.Footer.PeerID)
}
msgSig, err := base64.RawURLEncoding.DecodeString(pieces[2])
@@ -174,25 +171,26 @@ func (auth *PublicMode) Unpack(token string) (publicToken *PublicToken, err erro
return nil, err
}
- msg, err := Verify(peerKey.Public, msgSig, rawfooter)
+ payload, err := Verify(peerKey.Public, msgSig, rawfooter)
if err != nil {
return nil, err
}
- err = json.Unmarshal(msg, &publicToken.Token)
+ err = json.Unmarshal(payload, &msg.Payload)
if err != nil {
return nil, err
}
- err = publicToken.Token.Validate(auth.our.ID, peerKey)
+ err = msg.Payload.Validate(auth.our.ID, peerKey)
if err != nil {
return nil, err
}
- publicToken.Data, err = base64.StdEncoding.DecodeString(publicToken.Token.Data)
+ data, err := base64.StdEncoding.DecodeString(msg.Payload.Data.(string))
if err != nil {
return nil, err
}
+ msg.Payload.Data = data
- return publicToken, nil
+ return msg, nil
}
diff --git a/lib/paseto/v2/public_mode_test.go b/lib/paseto/v2/public_mode_test.go
index 2967b4bd..6375ba1c 100644
--- a/lib/paseto/v2/public_mode_test.go
+++ b/lib/paseto/v2/public_mode_test.go
@@ -9,6 +9,7 @@ import (
"net/http"
"testing"
+ "git.sr.ht/~shulhan/pakakeh.go/lib/paseto"
"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)
@@ -17,7 +18,7 @@ func TestPublicMode_UnpackHTTPRequest(t *testing.T) {
senderSK, _ := hex.DecodeString("e9ae9c7eae2fce6fd6727b5ca8df0fbc0aa60a5ffb354d4fdee1729e4e1463688d2160a4dc71a9a697d6ad6424da3f9dd18a259cdd51b0ae2b521e998b82d36e")
senderPK, _ := hex.DecodeString("8d2160a4dc71a9a697d6ad6424da3f9dd18a259cdd51b0ae2b521e998b82d36e")
- ourKey := Key{
+ ourKey := paseto.Peer{
ID: "sender",
Private: ed25519.PrivateKey(senderSK),
Public: ed25519.PublicKey(senderPK),
@@ -96,6 +97,6 @@ func TestPublicMode_UnpackHTTPRequest(t *testing.T) {
continue
}
- test.Assert(t, c.desc, c.expData, got.Data)
+ test.Assert(t, c.desc, c.expData, got.Payload.Data)
}
}
diff --git a/lib/paseto/v2/public_token.go b/lib/paseto/v2/public_token.go
deleted file mode 100644
index 446eb12b..00000000
--- a/lib/paseto/v2/public_token.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-// SPDX-FileCopyrightText: 2020 Shulhan <ms@kilabit.info>
-
-package pasetov2
-
-// PublicToken contains the unpacked public token.
-type PublicToken struct {
- Token JSONToken
- Footer JSONFooter
- Data []byte
-}