Replies: 8 comments 10 replies
-
I'm also interested in that subject :) |
Beta Was this translation helpful? Give feedback.
-
I am also looking for solution to authenticate my external service (micro-service written in go) with token from auth.js. Please keep in mind that I have no idea about security and there is a chance that my solution created (unintentionally) some kind of security vulnerabilities. Auth.js file for reference: I wasn't able to do it with default encode/decode method (no idea why tbh, tried debugging, without success), so I copied encode/decode methods from auth.js repository (with small modifications, JWT_SALT was hardcoded, I'm taking it from env. If you want to verify jwt in another service, remember that we are adding salt to the provided secret ( If service won't have access to the jwt secret, then I think we would need to modify encode / decode methods to use asymmetric signing key method and expose public key trough the additional endpoint (JWKS endpoint i guess). Or we can just switch to the the service that will handle all of it for us, clerk, supabase, firebase, as, if I recall correctly, this is not recommended usage of the library by authors. And there is always keycloak. Auth.ts: import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { type GetServerSidePropsContext } from "next";
import {
getServerSession,
type DefaultSession,
type NextAuthOptions,
} from "next-auth";
import {
type JWT,
type JWTDecodeParams,
type JWTEncodeParams,
} from "next-auth/jwt/types";
import GoogleProvider from "next-auth/providers/google";
import { env } from "~/env.mjs";
import { prisma } from "~/server/db";
// additional
import { hkdf } from "@panva/hkdf";
import crypto from "crypto";
import { EncryptJWT, jwtDecrypt } from "jose";
/**
* Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
* object and keep type safety.
*
* @see https://next-auth.js.org/getting-started/typescript#module-augmentation
*/
declare module "next-auth" {
interface Session extends DefaultSession {
user: {
id: string;
haha: number;
} & DefaultSession["user"];
}
}
export const authOptions: NextAuthOptions = {
secret: env.JWT_SECRET,
session: {
strategy: "jwt",
},
callbacks: {
session({ session, token }) {
session.user.id = token.id as string;
session.user.id;
return session;
},
jwt({ token, user }) {
// we can inject roles to the jwt here
return token;
},
},
jwt: {
decode: jwtDecode,
encode: jwtEncode,
},
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
}),
],
};
/**
* Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file.
*
* @see https://next-auth.js.org/configuration/nextjs
*/
export const getServerAuthSession = (ctx: {
req: GetServerSidePropsContext["req"];
res: GetServerSidePropsContext["res"];
}) => {
return getServerSession(ctx.req, ctx.res, authOptions);
};
/*
* copied from NextAuth.js source code
*/
const JWT_SALT = env.JWT_SALT;
const JWT_SECRET = env.JWT_SECRET;
const MAX_AGE = 30 * 24 * 60 * 60; // 30 days
const now = () => (Date.now() / 1000) | 0;
export async function jwtEncode(params: JWTEncodeParams) {
const { token = {} } = params;
const encryptionSecret = await getDerivedEncryptionKey(JWT_SECRET.toString());
return await new EncryptJWT(token)
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
.setIssuedAt()
.setExpirationTime(now() + MAX_AGE)
.setJti(crypto.randomUUID())
.encrypt(encryptionSecret);
}
export async function jwtDecode(params: JWTDecodeParams): Promise<JWT | null> {
const { token } = params;
if (!token) return null;
const encryptionSecret = await getDerivedEncryptionKey(JWT_SECRET.toString());
const { payload } = await jwtDecrypt(token, encryptionSecret, {
clockTolerance: 15,
});
return payload;
}
async function getDerivedEncryptionKey(secret: string) {
return await hkdf("sha256", secret, "", JWT_SALT, 32);
}
export async function createJWT() {
const encryptionSecret = await getDerivedEncryptionKey(JWT_SECRET.toString());
return await new EncryptJWT({ hello: "world" })
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
.setIssuedAt()
.setExpirationTime(now() + MAX_AGE)
.setJti(crypto.randomUUID())
.encrypt(encryptionSecret);
}
export async function createJWTFromObject(objectToSign: any) {
const encryptionSecret = await getDerivedEncryptionKey(JWT_SECRET.toString());
const createdJWT = await new EncryptJWT(objectToSign)
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
.setIssuedAt()
.setExpirationTime(now() + MAX_AGE)
.setJti(crypto.randomUUID())
.encrypt(encryptionSecret);
return createdJWT;
}
export async function getPayloadFromJwtToken(
jwtToken: string | undefined
): Promise<JWT | null> {
return jwtDecode({
token: jwtToken,
secret: JWT_SECRET.toString(),
});
} endpoint to get token: import { type NextApiRequest, type NextApiResponse } from "next";
import { createJWTFromObject } from "~/server/auth";
// --data-urlencode '[email protected]'
// --data-urlencode 'secret=password'
// --data-urlencode 'csrfToken=820f09779d39cf6b3e50648702aad42d67a6c5df46e4462bd2d35abc0d7551ca'
export default async function index(req: NextApiRequest, res: NextApiResponse) {
const { id, secret, csrfToken } = req.body;
let ok = await isCsrfTokenValid(csrfToken);
if (!ok) {
return res.status(403).json({ error: "idk" });
}
ok = validateCredentials(id, secret);
if (!ok) {
return res.status(401).json({ error: "Not authenticated" });
}
const contentToSign = {
id: 1,
name: "John Doe",
email: "[email protected]",
};
const encodedData = await createJWTFromObject(contentToSign);
return res.status(200).json({ token: encodedData });
}
const validateCredentials = (id: string, secret: string) => {
return true;
};
const isCsrfTokenValid = async (csrfToken: string) => {
return true;
}; protected endpoint: import { type NextApiRequest, type NextApiResponse } from "next";
import { getPayloadFromJwtToken } from "~/server/auth";
export default async function GET(req: NextApiRequest, res: NextApiResponse) {
console.log("req", req);
// idk whats wrong with this atm
// getToken expects the authorization header to be encoded...
// const tokenSplitted = req.headers.authorization?.split(" ");
// req.headers.authorization = tokenSplitted
// ? tokenSplitted[0] + " " + encodeURIComponent(tokenSplitted[1] ?? "")
// : "";
// const tokenData = await getToken({ req });
const tokenData = await getDataTokenManual(req, res);
if (!tokenData) {
return res.status(401).json({ error: "Not authenticated" });
}
return res.status(200).json({ tokenData });
}
const getDataTokenManual = async (
req: NextApiRequest,
res: NextApiResponse
) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token || token.trim().length === 0) {
return res
.status(401)
.json({ error: "Missing or invalid authorization header" });
}
const tokenContent = await getPayloadFromJwtToken(token);
return tokenContent;
}; bash script to verify endpoint: #!/bin/bash
baseUrl="http://localhost:3000/api"
token=$(curl --silent $baseUrl/microservice-auth | jq -r ".token")
echo "generated token from endpoint: "
echo $token
responseNoAuth=$(curl --silent $baseUrl/i-am-protected)
echo "response without auth:"
echo "$responseNoAuth"
responseWithAuth=$(curl --silent -H "Authorization: Bearer $token" $baseUrl/i-am-protected)
echo "response with auth: "
echo $responseWithAuth |
Beta Was this translation helpful? Give feedback.
-
Update: variable that i referenced as JWT_SALT is in fact JWT_INFO. |
Beta Was this translation helpful? Give feedback.
-
Note that this currently out of scope for the library. Auth.js is not an Identity Provider for your services. This might change in the future. |
Beta Was this translation helpful? Give feedback.
-
Hey guys, I just noticed that the string I'm getting is just encrypted signed JSON, not encrypted signed JWT. The diferrence is that, after encryption in case of ecrypted JSON we are getting claims (payload) and in case of ecrypted JWT we are getting JWT which then can be verified. Right now I'm rewriting to sign jwt using RSA, encrypt jwt using symmetric key and expose jwt key using jwks endpoint. I will post here update if I get it working between authjs and go. |
Beta Was this translation helpful? Give feedback.
-
So, here is small update: With custom encode / decode function we changed functionality. To be honest i found a little bit misleading fact, that input parameter to the encrypt / decrypt function is called "token", when it is normal js object. For beginner in these topics, word "token" suggest JWT token, not unsigned json. With current setup we are able to:
Authjs token is stored in the cookies, so in the endpoint body (see /api/token for example), we can call authjs function getToken, from which we can get raw encrypted jwt token, or our object with data (claims) from encrypted validated token that came from cookie. Data in the token will be as safe as encryption algorithm and key (you need to have key in order to encrypt token). Again, I'm new to this and you have to keep in mind that everything I said may be wrong. I think with this base I can communicate safely with my microservice using data from authjs. RN I can't provide repo url, but if I start anything (for sure I will create example from this solution when I make sure everything works and it is usable) i will drop the comment in this discussion. [Update] scripts to generate keys: # rsa
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048 && openssl rsa -in private_key.pem -pubout -out public_key.pem
# secret
openssl rand -base64 32 Auth.ts import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { type GetServerSidePropsContext } from "next";
import {
getServerSession,
type DefaultSession,
type NextAuthOptions,
} from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { env } from "~/env.mjs";
import { prisma } from "~/server/db";
// additional
import {
createAndEncryptJwtFromPayload,
decryptAndDecodeJwt,
} from "./auth-tokens";
/**
* Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
* object and keep type safety.
*
* @see https://next-auth.js.org/getting-started/typescript#module-augmentation
*/
declare module "next-auth" {
interface Session extends DefaultSession {
user: {
id: string;
haha: number;
} & DefaultSession["user"];
}
}
export const authOptions: NextAuthOptions = {
secret: env.JWT_SECRET,
session: {
strategy: "jwt",
},
callbacks: {
session({ session, token }) {
session.user.id = token.id as string;
session.user.id;
return session;
},
jwt({ token, user }) {
return token;
},
},
jwt: {
async decode(params) {
const decoded = await decryptAndDecodeJwt(params.token ?? "");
return decoded.payload;
},
async encode(params) {
const encrypted = await createAndEncryptJwtFromPayload(params.token);
return encrypted;
},
},
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
}),
],
};
/**
* Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file.
*
* @see https://next-auth.js.org/configuration/nextjs
*/
export const getServerAuthSession = (ctx: {
req: GetServerSidePropsContext["req"];
res: GetServerSidePropsContext["res"];
}) => {
return getServerSession(ctx.req, ctx.res, authOptions);
}; auth-tokens.ts import crypto from "crypto";
import {
compactDecrypt,
CompactEncrypt,
exportJWK,
importPKCS8,
importSPKI,
jwtVerify,
SignJWT,
type CompactDecryptResult,
type JWK,
type JWTVerifyResult,
type KeyLike,
} from "jose";
import { now } from "next-auth/client/_utils";
const MAX_AGE = 60 * 60 * 24 * 30; // 30 days
const SIGN_PUBLIC_KEY =
process.env.SIGN_PUBLIC_KEY ??
`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumC6JnlByf8S9cVbEZkz
vBg1LBY1EPQt4+RIsgOo159amsDz9p/5DuOY2IZHabcHFdT4uHhoCYGGFtYh06uA
oH0s15QEgBlbkoJqigEhClzCwmetHi3Vp5NKfoV7ZAyOWEjRdXj3D2gAq0g52tv3
Ll9j77qnMs3+xZqTV8SgRAwVQXDbYMccPYKw91tMlknO8FQId4hxaHh9JAjPIMmf
AGHajI+siNV/NYfDSgYb6TnJPbzntSO+n9mrhe74u56qeOcIjXQjP+2/7mwNAlcx
xwFYLO2IpPdu2bGfFP7uspw1dRo3hakCC49lhmppzUsijloRubJViw5EVAXkOZsV
LQIDAQAB
-----END PUBLIC KEY-----`;
const SIGN_PRIVATE_KEY =
process.env.SIGN_PRIVATE_KEY ??
`-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6YLomeUHJ/xL1
xVsRmTO8GDUsFjUQ9C3j5EiyA6jXn1qawPP2n/kO45jYhkdptwcV1Pi4eGgJgYYW
1iHTq4CgfSzXlASAGVuSgmqKASEKXMLCZ60eLdWnk0p+hXtkDI5YSNF1ePcPaACr
SDna2/cuX2Pvuqcyzf7FmpNXxKBEDBVBcNtgxxw9grD3W0yWSc7wVAh3iHFoeH0k
CM8gyZ8AYdqMj6yI1X81h8NKBhvpOck9vOe1I76f2auF7vi7nqp45wiNdCM/7b/u
bA0CVzHHAVgs7Yik927ZsZ8U/u6ynDV1GjeFqQILj2WGamnNSyKOWhG5slWLDkRU
BeQ5mxUtAgMBAAECggEAOAP0TrXo+P3pw+Yx7cMe0mxwJrTx9At/c1vcIWyKJWwm
fhZ5/GiuITR0brdE5Vf2EDC3JDJECn9cPqbS+dw0x4d3n79D7wAwDoOV465dMNEa
U/V3CXf7G+df31Dc+f/wFEamdWkK0B2Q/emyQb4Ut2JCFf4CXHmfFeLUX++fv3Ni
trn/7mn06ewZH2olWxO2mcIxHPLryyg4F7nXRgujZiechadzOQSNrdt5pw5jhtiJ
/G0evne4/6fzDYWWP5iBzwByhkuG4mf5qsfd42gq7OubDiIOdlnWyRoWaUV9vzzl
0rwXiAYf6AMYWxAaSC/rTX0oaOwuQnzQ3ICoNgbgfQKBgQDncxMuS18y4lcDuvaZ
InhWg29IN83iKAAbWywp8P2KLBzNCGGjjvBT1ildbWP3gLrhRbO7n0vx/4WTg/TL
jVZgzw3S0XhEoJ+yIJkWvwDj8OQuCrgj97juX6k3yIUxps5g+EVg0PJZvZCq1afU
Y426Ow+Lphkg7czsY8EZ3ZCedwKBgQDOJb9xeXUgCSJOK35wFiliujb/PjvRLyHY
aHb4ckO2Stp01pfyzB99j0Kkk/eW2xoAF3KtyWpEsyK8PlOZIXUl1l9evi3/EDyf
xn/30bEEov19z6NGO5G+yNxWW+4czD2kP3YMf6N85/7NbhEoj2pP3CEn6bb6o7AP
QXd0xBseewKBgQDR1vk67ccAAUnW7fmJ2UC1XzDTjxSwyIdgRgcCd6XHUjtCgSjU
+1FVEUQrmnQZ/7Jvb5yGrORUIdDfb7HPovL/jnn/+z5JKj/fZcKmZsHN56dZPwq4
QW5/YGexVbG81HkkXbO5FXfEeSPiXxdsbl4ezFFJ0WvS+QbkA4UdiE4H/QKBgQC5
tTm8oN0JqhDUeMwjb2ODBBwNXmQGsz37NUiP0mf7bX1VKgiieo6XzxoLJx0xMlo+
4OC3fC4Wi4XsLP2kgoaFOpSZxhurnlXu3aRkfOvKMIbJ8ua0m29Vfp830QM1wCx7
rYvMAKQoHtrk95XPgzsTjEv2J85mBlXRuHlDoHKJxwKBgBhi5GMGjQUp9a/YMiCG
bqzI2X/WQPYumBVv924kv9Y3vUeHlTgUeP7pmsi98hFfLEtnBZguV9RTcReA40DX
PiK82wDKSUUbY6TdLz8cb1kecJYGjIAXuYS3sABWCDXhmLP4MUabQ3+RAT5UaWYw
fyu4rD4yfFrItJxmGrBHa7FH
-----END PRIVATE KEY-----`;
const ENCRYPT_SYMMETRIC_KEY =
process.env.ENCRYPT_SYMMETRIC_KEY ??
"RXOKqu6ZSEkYPsJY9hSY4H39Ajou95Gyrb7HknlQGnE=";
const alg = "RS256";
const pkcs8SignPriv = SIGN_PRIVATE_KEY;
const pkcs8SignPub = SIGN_PUBLIC_KEY;
let privKeySign: KeyLike | undefined = undefined;
let publicKeySign: KeyLike | undefined = undefined;
let jwk: JWK | undefined = undefined;
if (typeof window === "undefined") {
(async () => {
privKeySign = await importPKCS8(pkcs8SignPriv, alg);
publicKeySign = await importSPKI(pkcs8SignPub, alg);
jwk = await exportJWK(publicKeySign);
})();
}
const encryptKey = new TextEncoder().encode(ENCRYPT_SYMMETRIC_KEY).slice(0, 32);
export async function createAndEncryptJwtFromPayload(
payload: any
): Promise<string> {
const jwt = await createJWTFromPayload(payload);
const encryptedJWT = await encryptJWT(jwt);
return encryptedJWT;
}
export async function decryptAndDecodeJwt(
jwt: string
): Promise<JWTVerifyResult> {
const decoded = await decryptJWT(jwt);
const asPlain = await new TextDecoder().decode(decoded.plaintext);
const payload = await getPayloadFromJWT(asPlain);
return payload;
}
export async function createJWTFromPayload(payload: any): Promise<string> {
const jwt = await new SignJWT(payload)
.setJti(crypto.randomUUID())
.setIssuedAt(now() + 1)
.setExpirationTime(now() + MAX_AGE)
.setNotBefore(now() + 1)
.setSubject("")
.setProtectedHeader({ alg: "RS256" })
// @ts-expect-error initialized during server startup
.sign(privKeySign);
return jwt;
}
export async function getPayloadFromJWT(jwt: string): Promise<JWTVerifyResult> {
// @ts-expect-error initialized during server startup
const verifiedJWTResult = await jwtVerify(jwt, publicKeySign);
return verifiedJWTResult;
}
export async function encryptJWT(jwt: string): Promise<string> {
const jwtToEncrypt = new TextEncoder().encode(jwt);
const encryptedJWT = await new CompactEncrypt(jwtToEncrypt)
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
.encrypt(encryptKey);
return encryptedJWT;
}
export async function decryptJWT(jwt: string): Promise<CompactDecryptResult> {
const decrypted = await compactDecrypt(jwt, encryptKey);
return decrypted;
}
// TODO
export async function getJWKS() {
return {
keys: [
{
...jwk,
alg: "RS256",
use: "sig"
},
],
};
} /api/me endpoint import { type NextApiRequest, type NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
export default async function index(req: NextApiRequest, res: NextApiResponse) {
const rawToken = await getToken({ req, raw: true });
return res.status(200).json({ token: rawToken });
} /api/token endpoint import { type NextApiRequest, type NextApiResponse } from "next";
import { createAndEncryptJwtFromPayload } from "~/server/auth-tokens";
export default async function GET(req: NextApiRequest, res: NextApiResponse) {
const userData = {
id: "123",
name: "John Doe",
email: "[email protected]",
};
const encryptedJWT = await createAndEncryptJwtFromPayload(userData);
console.log("encodedData", userData);
return res.status(200).json({ token: encryptedJWT });
} /api/.well-known/jwks.json,ts import { type NextApiRequest, type NextApiResponse } from "next";
import { getJWKS } from "../../../server/auth-tokens";
export default async function GET(req: NextApiRequest, res: NextApiResponse) {
const jwks = await getJWKS();
return res.status(200).json(jwks);
} working with token in go: import (
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
)
const (
authJsToken = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..x3jyufvzeAq5S95h.KZrJp4VQrJ8EMGF-MksIvgEzRLbdI6mq0B4Jt1MZO5r_H2F57NWkbA5KBBrK4V9M9xgC_7dXkmRF_Mln8XEVe1C6QLhf6-eBKM-NnvmmbzMWJkVUyso9WMEUELP7ghVpL24PiFD18ral5XBQToGjrfXBmiSs7fNtPheJKMHbvzQqKVVg-odX9hcA1Fwib0FoLXeBJjpkQDrOm-wUWkiejt6czt4CgssHpSTnAsJB9Ov5Sd_XFGEi_3iTSGBgrsKWT4Df3DSiDnmbtz_Z4Xabp8L0cYN1my-BtcI_oLn6IXdhbhloGDv583R1aYL6jo_eKiEzEk_0Y9OeJTN-e7qktf78qJJ76vv17Z_4MzAKgrlWZexJl3A1NrRfopJDX5hwWyO7ldKSTUixHkULgjsqg6bH3e89Q1EROVMUlPl5Jcx8bZBBdlPFaHJq_apBJM_589CwS1jDw2_zHwaw5fpHsORhuvioKsIlDxXsPBjTuZ4W5CV15qMyDWtTD_jlJ0wYKv-LpLay8w3tyZPjsj7aqjWG-vMg4bEh10nJbinAUqPiwHV90TSnDkwM80P4x_erHPVS7-Sx10ySm7fBTTo1sa5IuocfvYgEu7dmqrO8p6KuHffyjlWHfxMawtZogDTX9gni4MxpfyFRM9pqvgOBJnGdCuj1vBwtxhSqSu2bDqaYpBA7WVKNc1A0RE-uz5Jcd8qpMtlThcvPbD0QyK6VJaOQMiW7805AlX-_gvWBxVK0phUbLfoDEe4OUyI6E-d27igaL6hpxpFoloWnxHczX_yLOS-yGtiXNt-PwfK6q2l8RCESTWsTL27mLhwMl8uC6ij03yqmDwLAa7V2nSc8KphxVVvP73F0gF6wfr3Y3CLIDOWa7ZGBqZ1-hKg8uNMUka6MTRAPSKOEH9HPSYXI6pdkFBsifwJMhVOYHTDYtQ.xkjByjlWbLBZFH2HR8MOEg"
pubKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumC6JnlByf8S9cVbEZkz
vBg1LBY1EPQt4+RIsgOo159amsDz9p/5DuOY2IZHabcHFdT4uHhoCYGGFtYh06uA
oH0s15QEgBlbkoJqigEhClzCwmetHi3Vp5NKfoV7ZAyOWEjRdXj3D2gAq0g52tv3
Ll9j77qnMs3+xZqTV8SgRAwVQXDbYMccPYKw91tMlknO8FQId4hxaHh9JAjPIMmf
AGHajI+siNV/NYfDSgYb6TnJPbzntSO+n9mrhe74u56qeOcIjXQjP+2/7mwNAlcx
xwFYLO2IpPdu2bGfFP7uspw1dRo3hakCC49lhmppzUsijloRubJViw5EVAXkOZsV
LQIDAQAB
-----END PUBLIC KEY-----`
)
var (
encryptionSymmetricKey = "RXOKqu6ZSEkYPsJY9hSY4H39Ajou95Gyrb7HknlQGnE="[:32]
)
func Test_decryptAndParseAuthjsToken(t *testing.T) {
decrypted := decryptAuthjsToken(t)
println(string(decrypted))
pubRsa, err := jwk.ParseKey([]byte(pubKey), jwk.WithPEM(true))
if err != nil {
return
}
if err != nil {
return
}
parsed, err := jwt.Parse(decrypted, jwt.WithKey(jwa.RS256, pubRsa))
if err != nil {
t.Fatal(err.Error())
}
log.Printf("parsed: %+v", parsed)
}
func decryptAuthjsToken(t *testing.T) []byte {
decrypted, err := jwe.Decrypt([]byte(authJsToken), jwe.WithKey(jwa.DIRECT, []byte(encryptionSymmetricKey)))
if err != nil {
t.Fatal(err.Error())
}
return decrypted
} |
Beta Was this translation helpful? Give feedback.
-
@balazsorban44 Is #6927 that could do this? |
Beta Was this translation helpful? Give feedback.
-
next-auth make a jwt token that is undocumented, opaque (hashes things before encryption , etc) , and isn't guaranteed to work with other systems. instead... re-encrypt the data in the token for your other system:
load mysecret in a standard way: this will work across many systems, and you can pass that bearer token around to microservices as needed internally |
Beta Was this translation helpful? Give feedback.
-
Question 💬
I am tring to authenticate a microservice to a next-auth website.
I tried setting up a credential provider to take in an id and secret however when I call
localhost:3000/api/auth/signin/api
, I get redirected to the signin page.Here is the credential provider, I have defined:
What I would like is to be able to have a service authenticate with a id and secret and get back a jwt so it can use it to authenticate on other api endpoints exposed by nextjs.
How can I do this?
How to reproduce ☕️
curl --location 'localhost:3000/api/auth/signin/api'
--header 'Content-Type: application/x-www-form-urlencoded'
--header 'Cookie: next-auth.callback-url=http%3A%2F%2Flocalhost%3A3000; next-auth.csrf-token=820f09779d39cf6b3e50648702aad42d67a6c5df46e4462bd2d35abc0d7551ca%7Caa887391e425137a7080a16fbc4b3fcee5552fb4320d19f0cd6b395ff5ab770d'
--data-urlencode 'id=[email protected]'
--data-urlencode 'secret=password'
--data-urlencode 'csrfToken=820f09779d39cf6b3e50648702aad42d67a6c5df46e4462bd2d35abc0d7551ca'
Contributing 🙌🏽
Yes, I am willing to help answer this question in a PR
Beta Was this translation helpful? Give feedback.
All reactions