Created
December 4, 2022 01:24
-
-
Save aegrumet/9ca3e13278b8543348bfdb270133512d to your computer and use it in GitHub Desktop.
Decrypt a NextAuth jwe from somewhere else
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/sha256" | |
"fmt" | |
"io" | |
"github.com/lestrrat-go/jwx/v2/jwa" | |
"github.com/lestrrat-go/jwx/v2/jwe" | |
"golang.org/x/crypto/hkdf" | |
) | |
func main() { | |
var err error | |
rawJwe := "raw jwe text" | |
nextAuthSecret := "next auth secret" | |
info := "NextAuth.js Generated Encryption Key" | |
// Step 1: Generate the decryption key with an hdkf lib | |
hash := sha256.New | |
kdf := hkdf.New(hash, []byte(nextAuthSecret), []byte(""), []byte(info)) | |
key := make([]byte, 32) | |
_, _ = io.ReadFull(kdf, key) | |
// Step 2: Decrypt with a JWE library. | |
// Here we use lestrrat-go/jwx, which parses the JWE and | |
// uses the JWE header info to choose the decryption algorithm. | |
decrypted, err := jwe.Decrypt([]byte(rawJwe), | |
jwe.WithKey(jwa.DIRECT, key)) | |
if err != nil { | |
fmt.Printf("failed to decrypt: %s", err) | |
return | |
} | |
fmt.Println(string(decrypted)) | |
} |
@alimorgaan, how do you get a decoded token? In simple how to use decode method?
Currently, I am getting this accessToken ya29.a0AcM612wgVtxPKK_s_YkCZxdgTQ8z4QEtMcIiAf87IvUP_uAvt04skTbPlbk8VsC2c0UDKGbhzLkxv0bV9_WFTfX3wAjXVPwOg8hw1hzmK2iYYDREpXZitf9Fsw-2vT640HmycqqH_8mShS0fUap1RTX77VxmV9UoKpsdfDh_aCgYKAd8SARASFQHGX2Min3G9lxVnC1aQxRE1xXXrAQ0175
So, is this an encoded token, or is this incomplete or half-taken?
updated code for go :
package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/joho/godotenv"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwe"
"github.com/lestrrat-go/jwx/v3/jwt"
"golang.org/x/crypto/hkdf"
)
func generateEncryptionKey() ([]byte, error) {
authSecret := os.Getenv("AUTH_SECRET")
if authSecret == "" {
return nil, fmt.Errorf("AUTH_SECRET not set")
}
salt := "authjs.session-token"
info := fmt.Sprintf("Auth.js Generated Encryption Key (%s)", salt)
// HKDF with SHA-256
hash := sha256.New
kdf := hkdf.New(hash, []byte(authSecret), []byte(salt), []byte(info))
key := make([]byte, 64)
if _, err := io.ReadFull(kdf, key); err != nil {
return nil, fmt.Errorf("failed to generate key: %w", err)
}
return key, nil
}
func jweToJwt(encryptedToken string) (string, error) {
key, err := generateEncryptionKey()
if err != nil {
return "", fmt.Errorf("key generation failed: %w", err)
}
// Decrypt JWE using DIRECT key encryption and A256GCM content encryption
decrypted, err := jwe.Decrypt([]byte(encryptedToken),
jwe.WithKey(jwa.DIRECT(), key))
if err != nil {
return "", fmt.Errorf("JWE decryption failed: %w", err)
}
var payload map[string]interface{}
if err := json.Unmarshal(decrypted, &payload); err != nil {
return "", fmt.Errorf("failed to parse payload: %w", err)
}
token := jwt.New()
for k, v := range payload {
token.Set(k, v)
}
signed, err := jwt.Sign(token, jwt.WithKey(jwa.HS256(), []byte(os.Getenv("AUTH_SECRET"))))
if err != nil {
return "", fmt.Errorf("JWT signing failed: %w", err)
}
return string(signed), nil
}
func validateTokenFromCookie(r *http.Request) (jwt.Token, error) {
cookie, err := r.Cookie("authjs.session-token")
if err != nil {
return nil, fmt.Errorf("no session cookie: %w", err)
}
// Convert JWE to JWT
jwtString, err := jweToJwt(cookie.Value)
if err != nil {
log.Error("Failed to convert JWE to JWT", "error", err)
return nil, err
}
// Verify JWT
token, err := jwt.Parse([]byte(jwtString),
jwt.WithKey(jwa.HS256(), []byte(os.Getenv("AUTH_SECRET"))),
jwt.WithValidate(true))
if err != nil {
return nil, fmt.Errorf("invalid JWT: %w", err)
}
// Check expiration
if exp, ok := token.Expiration(); ok && exp.Before(time.Now()) {
return nil, fmt.Errorf("token expired")
}
return token, nil
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
i'm using next-auth 5.0.0-beta.20, i think the best solution is to write your own encode and decode functions for next-auth, instead of depending on a salt that may change in the future, here is what i did.
Like this i can share the public key to all my services and verify the JWT anywhere