NextAuth.js: JWT secret breaks application - jwt

[I'm using Next.js (11.1.2) + NextAuth (4.0.0-beta.7) to signin into a Strapi API, using only Credentials provider (JWT).]
Whole auth flow is "working" with this [...nextauth].js:
import NextAuth from "next-auth"
import CredentialsProvider from 'next-auth/providers/credentials'
export default NextAuth({
providers: [
CredentialsProvider({
name: 'AppName',
credentials: {
email: {label: "Email", type: "text", placeholder: "daveglow#foomail.com"},
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const res = await fetch(process.env.CREDENTIALS_AUTH_URL, {
method: 'POST',
body: JSON.stringify(credentials),
headers: { "Content-Type": "application/json" }
})
const user = await res.json()
if (res.ok && user) {
return user
}
return null
}
})
],
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60 // 30 days
},
pages: {
signIn: '/signin',
signOut: '/signin',
error: '/signin'
},
})
But few seconds after the user login, terminal shows this message and drop the session:
[next-auth][warn][NO_SECRET] https://next-auth.js.org/warnings#no_secret
[next-auth][error][JWT_SESSION_ERROR] https://next-auth.js.org/errors#jwt_session_error decryption operation failed {
message: 'decryption operation failed',
stack: 'JWEDecryptionFailed: decryption operation failed\n'
So, I tried to add:
secret: process.env.SECRET, //I've created using $ openssl rand -base64 32
Then I get two different messages
Browser console:
[next-auth][error][CLIENT_FETCH_ERROR]
https://next-auth.js.org/errors#client_fetch_error
VS Code Terminal:
[next-auth][error][CALLBACK_CREDENTIALS_JWT_ERROR]
https://next-auth.js.org/errors#callback_credentials_jwt_error Signin in with credentials only supported if JWT strategy is enabled UnsupportedStrategy [UnsupportedStrategyError]: Signin in with credentials only supported if JWT strategy is enabled
I tried a couple different options, but It's been so confuse.
And now, I have no clue what to do. :(
Can you help me?

I upgraded to version 4.0.1. Fixed the issue.

It is probably a bug introduced in beta 7 version of next-auth as discussed in https://github.com/nextauthjs/next-auth/issues/3216

I am not an expert. But I think this is a current problem with the library NEXT auth 4.0.0
I could solve the problem using the version "next-auth": "^3.25.0".
and followed this tutorial

Related

After successfully deploying Next.js app on AWS Amplify, https://www.example.com/api/any-route showing below error in console

The app is deployed successfully but the API routes (/pages/api) are not working as expected, showing below error in the console.
Build is successful and deployed on aws-amplify, I have added environment variables correctly, don't know why this is happening?
Does aws-amplify doesn't support serverless functions writer inside /api folder??
{
"error": {
"message": "connect ECONNREFUSED 127.0.0.1:80",
"name": "Error",
"stack": "Error: connect ECONNREFUSED 127.0.0.1:80\n at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1148:16)",
"config": {
"url": "undefined/campaigns",
"method": "get",
"headers": {
"Accept": "application/json, text/plain, */*",
"User-Agent": "axios/0.21.4"
},
"auth": {},
"transformRequest": [null],
"transformResponse": [null],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1,
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
}
},
"code": "ECONNREFUSED"
}
}
Here is the code
import axios from 'axios';
import Cors from 'cors';
import rateLimit from '../../../utils/rate-limit';
import initMiddleware from '../../../lib/init-middleware';
// Initialize the cors middleware
const cors = initMiddleware(
Cors({
methods: ['POST'],
origin: ['https://www.example.com', /\.example\.com$/],
})
);
const limiter = rateLimit({
interval: 60 * 1000, // 60 seconds
uniqueTokenPerInterval: 500, // Max 500 users per second
});
const handler = async (req, res) => {
await cors(req, res);
if (req.method === 'GET') {
try {
await limiter.check(res, 50, 'CACHE_TOKEN');
const { data } = await axios.get(`${process.env.BASE_URL}/campaigns`, {
auth: {
username: process.env.MAIL_SERVER_USERNAME,
password: process.env.MAIL_SERVER_PASSWORD,
},
});
return res.status(200).json(data);
} catch (error) {
return res.status(429).json({ error });
}
} else {
try {
await limiter.check(res, 10, 'CACHE_TOKEN'); // 10 requests per minute
return res.status(200).json('not allowed');
} catch (err) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
}
};
export default handler;
I figured out that environment variables are not getting reflected, so did some googling; found this solution and it worked for me.
Add your desired environment variable in the Amplify Console-like normal (steps)
Update (or create) your next.config.js file with the environment variable you added in the Amplify Console. E.g if you created an environment variable named MY_ENV_VAR in the console in step 1) above, then you would add the following:
module.exports = {
env: {
MY_ENV_VAR: process.env.MY_ENV_VAR
}
};
Now after your next build you will be able to reference your environment variable (process.env.MY_ENV_VAR) in your SSR lambdas!
Here is the link: Github
Ran into the same problem Amplify only supports NextJS 11 at the moment. If you go with the default settings it will use the latest NextJS 12 and /api routes wont work, they return a 503 with a cloudformation permission error.
Specify next 11.1.3 in your package.json
https://aws.amazon.com/about-aws/whats-new/2021/08/aws-amplify-hosting-support-next-js-version-11/
I also faced that problem. Amplify does not support v12 of next. Simply downgrade your next.js version in package.json to v1.1.3 and your routes will work as normal.
Best regrets.
Artem Meshkov

Android Enterprises Device Enrollment Stuck with NodeJs Generated QR Code with Service Account Authentication

As mentioned in the google documents i have tested the following process
URL to quick start: https://colab.research.google.com/github/google/android-management-api-samples/blob/master/notebooks/quickstart.ipynb#scrollTo=pjHfDSb8BoBP
Create Enterprise
Create Policy
Enroll the device
Then I have used the NODEJS API of Android Enterprises to develop the server based solution, which is working fine as per the documentation for all the functions such as get, create, delete the policy, devices, enterprises.
The issue i am facing is with the QR code generated from NODE application, when i scan the QR code generated from NODEJS application, the device got stuck at system update.
Following is my Policy update function
router.post('/update/:id', async function(req, res) {
const {title,policy_body,update_mask,enroll_url} = req.body;
// here we are callng the android managment API to and then the response we will update to database
const amApiBody = {
name: policy_body.name,
updateMask:update_mask,
requestBody:policy_body
}
const policy_update_response = await amApi.updatePolicy(amApiBody);
const p = await policyModel.update(req.params.id,title,policy_update_response,enroll_url);
res.json(p)
});
AmAPI file
this.updatePolicy = async function (body)
{
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
});
const authClient = await auth.getClient();
google.options({auth: authClient});
// Get the list of available policies
const res = await androidmanagement.enterprises.policies.patch(body);
console.log('requestFinalBody=',body);
return res.data;
}
Following is my policy data obtained by running above function
policy_create_response= {
name: 'enterprises/LC019rjnor/policies/policy1',
version: '14',
applications: [
{
packageName: 'com.google.samples.apps.iosched',
installType: 'FORCE_INSTALLED',
autoUpdateMode: 'AUTO_UPDATE_HIGH_PRIORITY'
},
{
packageName: 'com.dekaisheng.courier',
installType: 'FORCE_INSTALLED',
autoUpdateMode: 'AUTO_UPDATE_HIGH_PRIORITY'
}
],
keyguardDisabledFeatures: [ 'KEYGUARD_DISABLED_FEATURE_UNSPECIFIED' ],
defaultPermissionPolicy: 'GRANT',
uninstallAppsDisabled: true,
keyguardDisabled: true,
tetheringConfigDisabled: true,
dataRoamingDisabled: true,
networkEscapeHatchEnabled: true,
bluetoothDisabled: true,
debuggingFeaturesAllowed: true,
funDisabled: true,
kioskCustomLauncherEnabled: true
}
Note i have exported the variable to the terminal as follows before running the app, the auth.json is the service account credential file.
export GOOGLE_APPLICATION_CREDENTIALS="/Users/Mac/Projects/wajid/mdm/server/env/auth.json"
Thanks for the help in advance
I figured out that in nodeJS API I was passing wrong property name of Policy value in the request body.
Code before fix
parent: this.getParent(policyName),
requestBody:{
“name”: “my_policy"
}
Code after fix
parent: this.getParent(policyName),
requestBody:{
"policyName”: “my_policy"
}

Error trying to get authenticated user email with googleapis and node.js

I'm implementing auth on my website using googleapis. The function plus.people.get doesn't work. I have seen it is deprecated on some forums but it's still documented at google which has me confused. The error I get is "Legacy People API has not been used in project 328985958128 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/legacypeople.googleapis.com then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry." The webpage doesn't even load. My code is
if (!req.body.token) return res.status(500).json({ type: 'error', message: 'No access token provided.' })
const OAuth2 = google.auth.OAuth2
const oauth2Client = new google.auth.OAuth2(keys.client_id, keys.client_secret)
google.options({ auth: oauth2Client });
const plus = google.plus('v1')
oauth2Client.setCredentials({
access_token: req.body.token
})
plus.people.get({
userId: 'me',
auth: oauth2Client
}, (error, response) => {
if (error)
console.log(error)
return res.status(500).json({ type: 'error',error })
const emails = (response.data || {}).emails
You are using google.plus('v1'), which has been deprecated
Instead you should use
const service = google.people({version: 'v1', auth: oauth2Client})
to create a service object.
To perform a request an additional auhtorization is not required anymore, so:
service.people.get({
userId: 'me'
}, (error, response) => {
...
})
Further information:
Creating a service account client with node.js
People API quide for node.js

rest-hapi standalone endpoint not returning handler results

Forgive me if it's a silly question, but the last time I coded in javascript was almost 20 years ago... I'm re-learning javascript these weeks and I'm not sure I got it all.
I'm using hapi with rest-hapi and want to add some standalone endpoints, basically translating the backend portion of this Autodesk tutorial form express.
I'm using the basic rest-hapi example main script, and tried to add a route with the following code:
//api/forge.js
module.exports = function(server, mongoose, logger) {
const Axios = require('axios')
const querystring = require('querystring')
const Boom = require('boom')
const FORGE_CLIENT_ID = process.env.FORGE_CLIENT_ID
const FORGE_CLIENT_SECRET = process.env.FORGE_CLIENT_SECRET
const AUTH_URL = 'https://developer.api.autodesk.com/authentication/v1/authenticate'
const oauthPublicHandler = async(request, h) => {
const Log = logger.bind('User Token')
try {
const response = await Axios({
method: 'POST',
url: AUTH_URL,
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
data: querystring.stringify({
client_id: FORGE_CLIENT_ID,
client_secret: FORGE_CLIENT_SECRET,
grant_type: 'client_credentials',
scope: 'viewables:read'
})
})
Log.note('Forge access token retrieved: ' + response.data.access_token)
return h.response(response.data).code(200)
} catch(err) {
if (!err.isBoom){
Log.error(err)
throw Boom.badImplementation(err)
} else {
throw err
}
}
}
server.route({
method: 'GET',
path: '/api/forge/oauth/public',
options: {
handler: oauthPublicHandler,
tags: [ 'api' ],
plugins: {
'hapi-swagger': {}
}
}
})
}
The code works and I can display the access_token in nodejs console, but swagger doesn't get the response:
At first I thought that an async function cannot be used as handler, but my hapi version is 17.4.0, and it supports async handlers.
What am I doing wrong?
It turns out it was an easy fix: I just needed to specify the Hapi server hostname in my main script!
The problem was with CORS, since Hapi used my machine name instead of localhost. Using
let server = Hapi.Server({
port: 8080,
host: 'localhost'
})
solved my problem.

How do I solve the keycloak refresh /#state on my angular7 project - i'm using keycloak-angular

I have also tried all varying combinations for web origins and valid redirect URIs
I login via keycloak and it continuously redirects me back and forth between my localhost application and this url: http://localhost:4200/#state=166446fd-daf6-4b76-b595-583c01c663df&session_state=57ead1f3-bf41-4117-9ddf-75e37c9248e7&code=8692b58b-0868-4762-b82e-acde9911dd34.57ead1f3-bf41-4117-9ddf-75e37c9248e7.1e8b5b9d-b590-453e-b396-62b46c18cc9f
I have tried it on firefox and chrome but with the same issue - it seems to be looking for the keycloak.json file in the network tab even though I can login to the correct realm via keycloak
GET http://localhost:4200/keycloak.json 404 (Not Found)
scheduleTask # zone.js:2969
ERROR An error happened during Keycloak initialization. core.js:1601
Unhandled Promise rejection: An error happened during Keycloak initialization. ; Zone: ; Task: Promise.then ; Value: An error happened during Keycloak initialization. undefined
static init(): Promise<any> {
const keycloakAuth: any = Keycloak({
url: 'http://localhost:8080/auth',
realm: 'ContractPortal',
clientId: 'secretkey2',
'ssl-required': 'external',
'public-client': true
});
KeycloakService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
keycloakAuth.init({onLoad: 'login-required'})
.success(() => {
console.log(keycloakAuth);
KeycloakService.auth.loggedIn = true;
KeycloakService.auth.authz = keycloakAuth;
KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl
+ '/realms/angular_keycloak/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
resolve();
})
.error(() => {
reject();
});
});
}
it'd be great if one of you guys can point me in the right direction to solve this issue...
I have found another similar question to what I have asked link but not sure how to implement this solution! this is my provider setup -
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializer,
multi: true,
deps: [KeycloakService]
}
],
but OP puts the following in his provider
providers: [
KeyCloakService,
AssetService,
{
provide: LocationStrategy,
useClass: PathLocationStrategy
}
]
Please let me know if you require any other information
For anybody that ran into the same issue - I misunderstood the keycloak.json file and did not know where to get it - so I was exporting the complete json file from keycloak but THIS is not how you should get it!!
First you have to go to your realm > clients and click on installation
then download the keycloak OIDC json file
then place it next to the index.html file in your application
This solved my issue - hope it helps somebody else
I was researching on the internet and it is the same case, for me my angular page is reloading automatically in infnite loop
I did some changes in keycloak-init.ts file (please find following code and change checkLoginIframe from true to false)
Old Code: keycloak.init ({onLoad: 'login-required', "checkLoginIframe": true})
New Code: keycloak.init ({onLoad: 'login-required', "checkLoginIframe": false})
this.loadingService.isLoading$.pipe(delay(100)).subscribe((data => {
this.loading = data
}))
this.isLogging = await this.keycloak.isLoggedIn();
type roleUser = Array<{ id: number, text: string }>
if (this.isLogging) {
this.userProfile = await this.keycloak.loadUserProfile();
this.userRoles = await this.keycloak.getUserRoles();
}else {
// this.clearSession();
}
if you are using "this.keycloak.loadUserProfile()" Remove this "clear session"