Next-Auth | When user object have too many items then session request is without data - reactjs

I will try to explain my problem as best as possible for me.
I'm using Strapi as a backend and Nextjs as a frontend.
For the authentication I using NextAuth.
[...nextauth].js:
const options = {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
username: { label: "Email", type: "email", placeholder: "jsmith" },
password: { label: "Password", type: "password" }
},
authorize: async (credentials) => {
try {
const user = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/auth/local`, {
identifier: credentials.username,
password: credentials.password,
});
if (user.data) {
return user.data
} else {
return null
}
} catch (error) {
const errorMessage = error.response.data.message[0].messages[0].message
throw new Error(errorMessage)
}
}
}),
],
database: process.env.NEXT_PUBLIC_DATABASE_URL,
session: {
jwt: true,
},
callbacks: {
jwt: async (token, user) => {
if (user) {
token.jwt = user.jwt;
token.user = user.user;
}
return Promise.resolve(token);
},
session: async (session, token) => {
session.jwt = token.jwt;
session.user = token.user;
return Promise.resolve(session);
},
},
pages: {
signIn: '/login',
error: '/login'
},
};
const Auth = (req, res) =>
NextAuth(req, res, options);
export default Auth;
When I send form with identifier and password I getting session response with user data and jwt.
Everything working well, but if user objects have few more objects assigned to him, then something goes wrong and the session is empty.
Example:
I creating in Strapi simple collection with just two fields - image field, and owner (relation to user). Every response with image contents few lines:
"photo": {
"_id": "60ca03fa20bd43033a53950e",
"name": "Zrzut ekranu 2021-06-16 o 14.59.48.png",
"alternativeText": "",
"caption": "",
"hash": "Zrzut_ekranu_2021_06_16_o_14_59_48_2bc223567b",
"ext": ".png",
"mime": "image/png",
"size": 170.69,
"width": 668,
"height": 636,
"url": "/uploads/Zrzut_ekranu_2021_06_16_o_14_59_48_2bc223567b.png",
"formats": {
"thumbnail": {
"name": "thumbnail_Zrzut ekranu 2021-06-16 o 14.59.48.png",
"hash": "thumbnail_Zrzut_ekranu_2021_06_16_o_14_59_48_2bc223567b",
"ext": ".png",
"mime": "image/png",
"width": 164,
"height": 156,
"size": 14.92,
"path": null,
"url": "/uploads/thumbnail_Zrzut_ekranu_2021_06_16_o_14_59_48_2bc223567b.png"
},
"small": {
"name": "small_Zrzut ekranu 2021-06-16 o 14.59.48.png",
"hash": "small_Zrzut_ekranu_2021_06_16_o_14_59_48_2bc223567b",
"ext": ".png",
"mime": "image/png",
"width": 500,
"height": 476,
"size": 121.79,
"path": null,
"url": "/uploads/small_Zrzut_ekranu_2021_06_16_o_14_59_48_2bc223567b.png"
}
},
When I add 1 or 2 items with image and try to login to user who own that items then everything is fine, I can log in and receive full user object and jwt token.
But when I add 3 items there, session object is empty, and I can't log in. I do not receiving user object and jwt token.
With 3 items
With 2 items
When I using postman, even with 50 items response is good and include full user object and jwt token.
I think is because response is too large or something like that, but I have no clue how to deal with it.
I working on localhost (both - frontend and backend) on MacOS.
Is there anyone who can help me find a solution for that problem?
Kind Regards,
GP

The reason of above issue is that in api response is passed to many data, and next-auth store that all data in JWT, and JWT is stored in full in cookie, cookie is limited to 4096 bytes, and that simply brake that limit and response.
The simplest way to sort it out is to save only most important data in JWT.
You can do that in callback function.

Related

How To Get Google User Profile Info Using Access Token From OAuth?

I am using ReactJS on the frontend: I successfully retrieved the access token from Google's Oauth service, but it doesn't give me any profile information for the 'current user'.
I tried using the /userInfo endpoint, ?access_token={my_access_token} query str, and some other solutions but I keep getting weird errors.
Currently I have:
const { access_token, refresh_token } = res.data; // now I have valid access_token
let userInfo = await axios.get("https://www.googleapis.com/oauth2/v3/userinfo",
{
headers: {
Authorization: `Bearer ${access_token}`
}
}); // ERROR WHEN MAKING THIS CALL.
My specific 401 error is as follows:
If there are better ways of getting user info (such as first/last name, email, etc. some sort of unique ID) please let me know.
You shouldnt rely on the user info endpoint to get user profile informaiton. As long as you are requesting the profile scope from the user, or even email. you should go though the People api. This will give you access to all of the users profile data.
GET https://people.googleapis.com/v1/people/me?personFields=names HTTP/1.1
Authorization: Bearer [YOUR_ACCESS_TOKEN]
Accept: application/json
response
{
"resourceName": "people/117dd2004755326722775346",
"etag": "%EgUBAi43PRoEAQIFBddyIMR3BzQkR2cnI1ZGc9",
"names": [
{
"metadata": {
"primary": true,
"source": {
"type": "PROFILE",
"id": "117200155326727753146"
}
},
"displayName": "Linda Lawton",
"familyName": "Lawton",
"givenName": "Linda",
"displayNameLastFirst": "Lawton, Linda",
"unstructuredName": "Linda Lawton"
},
{
"metadata": {
"source": {
"type": "CONTACT",
"id": "3faa96eb0118baa4be"
}
},
"displayName": "Linda Lawton",
"familyName": "Lawton",
"givenName": "Linda",
"displayNameLastFirst": "Lawton, Linda",
"unstructuredName": "Linda Lawton"
}
]
}

Excel Add-In keeps getting 401 error when trying to access Dynamics 365 Web Api

I am trying to develop a word add in in React with Typescript that allows me to access the dynamics 365 Web Api. The best example I've actually found was in a sample excel add in that accesses the graph api using MSAL 2.0 here https://github.com/OfficeDev/PnP-OfficeAddins/tree/master/Samples/auth/Office-Add-in-Microsoft-Graph-React. If I can get a successful get request to my Dynamics 365 api after modifying the example code I will port the code over to my Word Add in.
However I keep getting a 401 error when trying to go to https://saltrial.crm.dynamics.com/api/data/v9.1/
I get a response back when I simply paste this in a browser, but inside the add in I get 401 unauthorized. I also get 401 unauthorized when I paste the token received from the add in into postman with the header of Bearer + token. I have gotten a successful Access token however when I selected
I will show you my setup in Azure AD.... I have a client secret setup, but am not using it in my add in code.
Manifest
{
"id": "35c3a758-0edb-45f6-a97d-9c7180decd73",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": true,
"appId": "4f7xxxxxxxxxxxxxxxxxxxxxx310bf0",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2021-01-24T07:09:12Z",
"disabledByMicrosoftStatus": null,
"groupMembershipClaims": null,
"identifierUris": [
"api://localhost:3000/4fxxxxxxxxxxxxxxxxxxxxxxx"
],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "TrySSO",
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [
{
"adminConsentDescription": "Enable Office to call the add-in's web APIs with the same rights as the current user.",
"adminConsentDisplayName": "Office can act as the user",
"id": "5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f",
"isEnabled": true,
"lang": null,
"origin": "Application",
"type": "User",
"userConsentDescription": "Enable Office to call the add-in's web APIs with the same rights that you have.",
"userConsentDisplayName": "Office can act as you",
"value": "access_as_user"
}
],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2299-12-31T05:00:00Z",
"keyId": "570axxxxxxxxxxxxxxxxxxxxxxxxxxbcd1",
"startDate": "2021-01-28T04:11:05.086Z",
"value": null,
"createdOn": "2021-01-28T04:11:06.027737Z",
"hint": "mm-",
"displayName": "wordaddinsecret"
}
],
"preAuthorizedApplications": [
{
"appId": "ea5a67f6-b6f3-4338-b240-c655ddc3cc8e",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "d3590ed6-52b3-4102-aeff-aad2292ab01c",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "57fb890c-0dab-4253-a5e0-7188c88b2bb4",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "bc59ab01-8403-45c6-8796-ac3ef710b3e3",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
}
],
"publisherDomain": "salnewtrial.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://localhost:3000/login/login.html",
"type": "Spa"
},
{
"url": "https://login.microsoftonline.com/common/oauth2/nativeclient",
"type": "InstalledClient"
},
{
"url": "https://localhost:3000/login.html",
"type": "Web"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000007-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "78ce3f0f-a1ce-49c2-8cde-64b5c0896db4",
"type": "Scope"
}
]
},
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "14dad69e-099b-42c9-810b-d002981feec1",
"type": "Scope"
},
{
"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
"type": "Scope"
},
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null
}
Inside my login.ts in excel add in
(() => {
// The initialize function must be run each time a new page is loaded
Office.initialize = () => {
const msalInstance = new PublicClientApplication({
auth: {
clientId: "4f7f40ec-xxxxxxxxx-5d6b18310bf0",
authority: "https://login.microsoftonline.com/cd77a053-xxxxxxxxxx3402c0fd62", *This is tenant id
redirectUri: "https://localhost:3000/login/login.html", // Must be registered as "spa" type
},
cache: {
cacheLocation: "localStorage", // needed to avoid "login required" error
storeAuthStateInCookie: true, // recommended to avoid certain IE/Edge issues
},
});
// handleRedirectPromise should be invoked on every page load
msalInstance
.handleRedirectPromise()
.then((response) => {
// If response is non-null, it means page is returning from AAD with a successful response
if (response) {
Office.context.ui.messageParent(
JSON.stringify({ status: "success", result: response.accessToken })
);
} else {
// Otherwise, invoke login
msalInstance.loginRedirect({
scopes: [
"user.read",
"files.read.all",
"https://saltrial.crm.dynamics.com//user_impersonation",
],
});
}
})
.catch((error) => {
const errorData: string = `errorMessage: ${error.errorCode}
message: ${error.errorMessage}
errorCode: ${error.stack}`;
Office.context.ui.messageParent(
JSON.stringify({ status: "failure", result: errorData })
);
});
};
})();
Here is my API Call in Add in
import axios from 'axios';
export const getGraphData = async (url: string, accesstoken: string) => {
const response = await axios({
url: url,
method: 'get',
headers: {'Authorization': `Bearer ${accesstoken}`,
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
"Accept": "application/json",
"Content-Type": "application/json; charset=utf-8",
}
});
return response;
};
Code that calls my api function and passes the scopes and dynamics url. I console log the access token and put it into postman with header Bearer token, I get 401. Also the add-in displays 401 unauthorized in the task pane....Ignore any naming of functions referring to the graph api, I'm hitting the dynamics 365 api and hoping I don't get the 401 error. Thanks.
getFileNames = async () => {
this.setState({ fileFetch: "fetchInProcess" });
getGraphData(
// Get the `name` property of the first 3 Excel workbooks in the user's OneDrive.
"https://saltrial.crm.dynamics.com/api/data/v9.1/WhoAmI",
this.accessToken
)
.then(async (response) => {
await writeFileNamesToWorksheet(response, this.displayError);
this.setState({ fileFetch: "fetched", headerMessage: "Success" });
})
.catch((requestError) => {
// If this runs, then the `then` method did not run, so this error must be
// from the Axios request in getGraphData, not the Office.js in
// writeFileNamesToWorksheet
console.log("Access Token >>>>>>>>>>>>>>>>> ", this.accessToken);
this.displayError(requestError);
});
};

Extracting images data from Promise (ReactJS and Axios)

How do I get access to data stored as a Promise[] in ES6 array? I am loading an array from the backend and each object has a url for the image. So in order to render the image I have to async/await fetch the images for each object. Instead of returning a Base64 image, I get I promise with the image embeded deep in the promise. I need a way to read the image data. See image below for return object structure.
Please note that i use axios in the background and NOT the Fetch API
Below are the functions and service methods used to load the list and then the images in 2 steps.
1.1 Axios service method
listAllItems() {
return axios.get(API_URL + "docman/items/list/all");
}
1.2 ReactJS main function
async componentDidMount() {
try {
const items = await DocmanService.listAllItems();
const itemsData = items.data;
const data = await itemsData.map(item => {
const image = this.loadImage(item.image);
return { ...item, image: image }
})
this.setState({ items: data });
} catch (e) {
console.log(e);
}
}
2.1 Axios images service method
loadImage = (url) => {
return axios.get(API_URL + url, { responseType: 'arraybuffer' });
}
2.2 ReactJS image loader function
async loadImage(imageUrl) {
try {
return await ImageService.loadImage(imageUrl)
} catch (e) {
return '';
}
}
3.1 Json list fetched from backend
[
{
"id": "1",
"uuid": "1c0a33af-4e78-4823-b84e-f3b5464db2af",
"identifier": "EN ISO 8402",
"title": "EN ISO 8402 Quality Management - Vocabulary",
"folder": "e347fef9-0a76-4e62-b7f2-c78e9f88355b",
"media": "FILE",
"path": "document/id/105",
"image": "image/id/106",
"format": "PDF",
"authors": "",
"publishers": "",
"created": "2020-09-22T14:56:31.316+00:00",
"published": null,
"version": "1994",
"status": null
},
{
"id": "2",
"uuid": "16a0e658-b444-4b60-bf18-ba2786d5fb00",
"identifier": "ISO 14001",
"title": "ISO 14001 Environmenatl Management Systems - Requirements with Guidance for Use",
"folder": "e347fef9-0a76-4e62-b7f2-c78e9f88355b",
"media": "FILE",
"path": "document/id/107",
"image": "image/id/108",
"format": "PDF",
"authors": "",
"publishers": "",
"created": "2020-09-22T14:56:31.659+00:00",
"published": null,
"version": "2004",
"status": null
},
-
-
]
try doing image.then((data) => { // console.log(data) })
When you setState you are setting the new state with a list of objects with an image value that is a promise. Shouldn't you update the state only when at least one of the promise resolved to a real image?
Couldn't you do something like
this.loadImage(item.image).then((imageData=>setState(appropriate State)))?

Alexa skill not asking for permission (notification) when enabling the skill via voice

The skill in question asks for one permission when enabling in Web or app (Outbound Notification). But, when implemented Skill Enabled Event it's not asking user to give notification permission or not. Skill enablement works itself but permission is by default No. How to make alexa to ask for permission when enabling via voice?
Can Alexa prompt them via voice to enable the outbound notification?
skill.json
{
"manifest": {
"publishingInformation": {
"locales": {
"en-US": {
"summary": "test skill summary",
"examplePhrases": [
"Alexa, launch test skill",
"Alexa, open test skill",
"Alexa, start test skill"
],
"keywords": [
"test skill"
],
"name": "test skill",
"description": "test skill Description",
"smallIconUri": "",
"largeIconUri": "",
"updatesDescription": ""
}
},
"isAvailableWorldwide": true,
"testingInstructions": "n/a",
"category": "EVENT_FINDERS",
"distributionCountries": [],
"automaticDistribution": {
"isActive": false
}
},
"apis": {
"custom": {
"endpoint": {
"uri": "arn:aws:lambda:us-east-1:"
},
"interfaces": []
}
},
"manifestVersion": "1.0",
"privacyAndCompliance": {
"allowsPurchases": false,
"locales": {
"en-US": {
"privacyPolicyUrl": "",
"termsOfUseUrl": ""
}
},
"isExportCompliant": true,
"containsAds": false,
"isChildDirected": false,
"usesPersonalInfo": false
},
"events": {
"endpoint": {
"uri": "arn:aws:lambda:us-east-1:"
},
"publications": [
{
"eventName": "AMAZON.MessageAlert.Activated"
},
{
"eventName": "AMAZON.MediaContent.Available"
}
],
"regions": {
"NA": {
"endpoint": {
"uri": "arn:aws:lambda:us-east-1:",
"sslCertificateType": "Trusted"
}
}
},
"subscriptions": [
{
"eventName": "SKILL_PROACTIVE_SUBSCRIPTION_CHANGED"
},
{
"eventName": "SKILL_ENABLED"
},
{
"eventName": "SKILL_DISABLED"
},
{
"eventName": "SKILL_PERMISSION_ACCEPTED"
},
{
"eventName": "SKILL_PERMISSION_CHANGED"
},
{
"eventName": "SKILL_ACCOUNT_LINKED"
}
]
},
"permissions": [
{
"name": "alexa::devices:all:notifications:write"
}
]
}
}
Thank you for the help
There may be a different way, but once you are in the skill I believe you will need to send an ask for permissions card. As I understand it the idea is to make sure that Amazon is involved as a third party permissions granter. This will pop a permissions request in the Alexa app on the users phone. This added layer of security just makes sure the customer saw exactly what permissions they were granting.
You can do this a few different ways in your skill. You could check the first time that the user connects and keep track of that first connection in a persistent customer data layer. Or you could just check if the user has permission when you go to use that part of the skill. If they don't respond telling the customer you sent them a card to grant permissions.
Here is more info on permission cards:
https://developer.amazon.com/en-US/docs/alexa/custom-skills/request-customer-contact-information-for-use-in-your-skill.html#permissions-card-for-requesting-customer-consent
To run reminders via a lambda, other permissions are probably the same format.
const CreateReminderIntent = {
canHandle(handlerInput) {
const { request } = handlerInput.requestEnvelope;
return request.type === 'IntentRequest' && request.intent.name === 'CreateReminderIntent';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
const consentToken = requestEnvelope.context.System.user.permissions
&& requestEnvelope.context.System.user.permissions.consentToken;
if (!consentToken) {
return handlerInput.responseBuilder
.addDirective({
type: "Connections.SendRequest",
name: "AskFor",
payload: {
"#type": "AskForPermissionsConsentRequest",
"#version": "1",
"permissionScope": "alexa::alerts:reminders:skill:readwrite"
},
token: "<string>"
})
.getResponse();
}
try {
const speechText = "Great! I've scheduled a reminder for you";
const ReminderManagementServiceClient = serviceClientFactory.getReminderManagementServiceClient();
const reminderPayload = {
"trigger": {
"type": "SCHEDULED_RELATIVE",
"offsetInSeconds": "10",
"timeZoneId": "Europe/London"
},
"alertInfo": {
"spokenInfo": {
"content": [{
"locale": "en-GB",
"text": "Wash the dog"
}]
}
},
"pushNotification": {
"status": "ENABLED"
}
};
await ReminderManagementServiceClient.createReminder(reminderPayload);
return responseBuilder
.speak(speechText)
.getResponse();
} catch (error) {
console.error(error);
return responseBuilder
.speak('Uh Oh. Looks like something went wrong.')
.getResponse();
}
}
};

Ask for how to get user profile after callback

My web application is using Single Sign On (SSO) service from IBM Bluemix. This is the credentials info of my SSO service:
{
"SingleSignOn": [
{
"credentials": {
"secret": "MYSECRET",
"tokenEndpointUrl": "https://adminwebsso-jjjfvxi6wy-cq17.iam.ibmcloud.com/idaas/oidc/endpoint/default/token",
"authorizationEndpointUrl": "https://adminwebsso-jjjfvxi6wy-cq17.iam.ibmcloud.com/idaas/oidc/endpoint/default/authorize",
"issuerIdentifier": "adminwebsso-jjjfvxi6wy-cq17.iam.ibmcloud.com",
"clientId": "MYCLIENTID",
"serverSupportedScope": [
"openid"
]
},
"syslog_drain_url": null,
"volume_mounts": [],
"label": "SingleSignOn",
"provider": null,
"plan": "professional",
"name": "VA-Admin-Console-R1-SSO",
"tags": [
"security",
"ibm_created",
"ibm_dedicated_public"
]
}
]
}
From my Application, I redirect to Login page of IBM like URL:
https://adminwebsso-jjjfvxi6wy-cq17.iam.ibmcloud.com/idaas/oidc/endpoint/default/authorize?response_type=code&client_id=MYCLIENTID&redirect_uri=http://localhost/callbackā‰»ope=openid%20openid
After login success IBM redirect to my web application, I can get parameter "code" from callback URL (http://localhost/callback?scope=openid&code=bngM6aV5cYHAvhv7wLAM5QSWFDARn7).
From there, I use the "code" to to get user profile. I have try to use AJAX to get user profile:
var settings = {
"async": true,
"crossDomain": true,
"url": "https://idaas.ng.bluemix.net/sps/oauth20sp/oauth20/token",
"method": "POST",
"headers": {
"content-type": "application/x-www-form-urlencoded",
"authorization": "Basic RXhhbXBsZV9BcHBJRDpWUFlndEdXRlRvYVpZSUNTRzhJeVZFV000bUZicGpsU2t4RlRRbzlySkRGZDdzckc=",
"cache-control": "no-cache"
},
"data": {
"client_secret": "MYSECRET",
"grant_type": "authorization_code",
"redirect_uri": "http://localhost/callback",
"code": "bngM6aV5cYHAvhv7wLAM5QSWFDARn7",
"client_id": "MYCLIENTID"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
From ajax post above, I have tried to use the "code" from callback, but I've got an error message:
500 Error: Failed to establish a backside connection
I've got stuck here and don't know how to get user profile from SSO.
With in the SSO Service there are some inbuilt macros that can be used to get the user name, for more information please see.
https://console.bluemix.net/docs/services/SingleSignOn/customizing_pages.html#customizing_pages
#USERNAME# The user ID of the authenticated user.

Resources