I'm attempting to swap the default auth scheme in Blitz.js with a passport-azure-ad scheme, using the OIDCStrategy. I'm getting an error that I'm not sure about and would appreciate any help! I've created a new file under src/pages/auth/openid.tsx and into inserted the following code:
import { passportAuth } from "#blitzjs/auth"
import { api } from "src/blitz-server"
import { OIDCStrategy } from "passport-azure-ad"
const users: Array<{ oid: string }> = []
var findByOid = function (oid, fn) {
console.log("failing")
for (var i = 0, len = users.length; i < len; i++) {
const user = users[i]
console.log("we are using user: ", user)
if (user && user.oid === oid) {
return fn(null, user)
}
}
return fn(null, null)
}
export default api(
passportAuth({
successRedirectUrl: "/",
errorRedirectUrl: "/",
strategies: [
{
strategy: new OIDCStrategy(
{
identityMetadata:
"https://login.microsoftonline.com/<tenant-nam>.onmicrosoft.com/v2.0/.well-known/openid-configuration",
clientID: <client-id>,
responseType: "code id_token",
responseMode: "form_post",
redirectUrl: "http://localhost:3000/auth/openid/callback",
allowHttpForRedirectUrl: true,
clientSecret: "<client-secret>",
validateIssuer: false,
passReqToCallback: true,
scope: ["profile", "offline_access", "https://graph.microsoft.com/mail.read"],
loggingLevel: "info",
nonceMaxAmount: 5,
useCookieInsteadOfSession: false,
cookieEncryptionKeys: [
{ key: "12345678901234567890123456789012", iv: "123456789012" },
{ key: "abcdefghijklmnopqrstuvwxyzabcdef", iv: "abcdefghijkl" },
],
},
function (iss, sub, profile, accessToken, refreshToken, done) {
if (!profile.oid) {
return done(new Error("No oid found"), null)
}
// asynchronous verification, for effect...
process.nextTick(function () {
findByOid(profile.oid, function (err, user) {
if (err) {
return done(err)
}
if (!user) {
// "Auto-registration"
users.push(profile)
return done(null, profile)
}
return done(null, user)
})
})
}
),
},
],
})
)
I believe the configuration is good because I can run the example from passport-azure-ad from the github examples. The only change I make is that I set redirectUrl: "http://localhost:3000/auth/openid/callback", instead of redirectUrl: ".../return", per the blitz.js third party auth documentation. The tenantname, client_id, client_secret are redacted but I do set them to the correct values. I have also verified that the app registration is correctly set with the correct redirect uri.
I run blitz dev and when I go to the http://localhost:3000/auth/openid route I get the following error.
Here is the console output that is produced:
As you can see there is a Module not found: Can't resolve './src/build', this error only occurs if I go to the auth/openid page but the app is able to load.
On initial build or on forced restart of the app, the first user login does not update state. When a code change is made and it re-freshes the app, user login updates state in the expected manner.
This is the initial build console.log("stateUser", stateUser) :
stateUser
This is the refreshed console.log("stateUser", stateUser) :
stateUser Array [
Object {
"email": "this#email.com",
"id": 4,
"password": "",
"userToken": "devToken",
"username": "a",
},
]
This is my setState call which is nested inside of a React.useMemo():
const authContext = React.useMemo(() => ({
signIn: async (foundUser) => {
// console.log(foundUser);
userInfo = foundUser;
const userToken = String(foundUser[0].userToken);
const userName = foundUser[0].username;
foundUserState = foundUser;
setStateUser(foundUserState);
loginStateUpdater(foundUser);
// console.log(foundUser);
// console.log("aaaaaa", stateUser, "this is the stateUser")
try {
await AsyncStorage.setItem("userToken", userToken);
} catch (e) {
console.log(e);
}
// console.log('user token: ', userToken);
dispatch({ type: "LOGIN", id: userName, token: userToken });
},
My solution was to ditch AsyncStorage and move to something secure. I used Firebase Auth
I am trying to upload files to server in react-native application with axios as http handler.
My code stands as:
let promises = [];
const body = new FormData();
console.log(body);
body.append('device_name', this.state.deviceInfo.DeviceName);
body.append('device_id', this.state.deviceInfo.DeviceID);
body.append('device_ip', this.state.deviceInfo.DeviceIP);
body.append('device_os', this.state.deviceInfo.DeviceOS);
body.append('upload_type', 'user');
body.append('user_name', user.Email);
body.append('file1', {
uri: this.state.newImageUrl.uri,
name: 'test.jpg',
type: 'image/jpg',
});
promises.push(
apiUploadDocs(body)
.then(res => {
profileImageName = res[0].NewFileName;
})
.catch(err => {
console.log('this error', err);
}),
);
My apiUploadDocs is as :
export const apiUploadDocs = body => {
return new Promise((resolve, reject) => {
axios
.post(ApiRoutes.uploadDocs, body,{headers:{'content-Type': `multipart/form-data`}})
.then(res => {
console.log('upload success');
console.log(res);
})
.catch(err => {
console.log('upload error', err);
if (err.response) {
}
reject(Constant.network.networkError);
});
});
};
Every assigned variable has correct values upon logging and the api is working good when I try to upload from Postman.
But this snippet here results in an error which is undefined when logged.
I have tried trimming the 'file://' from the uri, as suggested by some answers here in stackoverflow.
I cant figure it out. Can you help me finding whats wrong here??
PS: The body when logged is:
{
"_parts":[
[
"device_name",
"sdk_gphone_x86"
],
[
"device_id",
"xxxxxxxxxxxxx"
],
[
"device_ip",
"10.0.2.xx"
],
[
"device_os",
"goldfish_x86"
],
[
"upload_type",
"user"
],
[
"user_name",
"xxxxx#gmail.com"
],
[
"file1",
[
"Object"
]
]
]
}
if it is of any reference.
I've found a link to uploading image in react-native.
https://aboutreact.com/file-uploading-in-react-native/
This might be of some help to you.
let uploadImage = async () => {
//Check if any file is selected or not
if (singleFile != null) {
//If file selected then create FormData
const fileToUpload = singleFile;
const data = new FormData();
data.append('name', 'Image Upload');
data.append('file_attachment', fileToUpload);
let res = await fetch(
'http://localhost//webservice/user/uploadImage',
{
method: 'post',
body: data,
headers: {
'Content-Type': 'multipart/form-data; ',
},
}
);
let responseJson = await res.json();
if (responseJson.status == 1) {
alert('Upload Successful');
}
} else {
//if no file selected the show alert
alert('Please Select File first');
}
};
I have been staring at these action creators:
import * as types from './constants';
import * as endpoints from 'endpoints';
import * as requester from 'services/Requester';
import * as helpers from 'account-settings/helpers/data-helpers';
export function fetchPrefences({Key}) {
return dispatch => {
const url = `${endpoints.v2.INDIVIDUALS}/${Key}/preferences`;
requester.sendGet(url).then(data => {
const payload = helpers.sortPreferences(data);
dispatch({
type: types.SET_USER_PREFERENCES,
payload,
});
});
};
}
export function fetchTopics() {
return dispatch => {
requester.sendGet(endpoints.TOPICS_OF_CONCERN).then(data => {
dispatch({
type: types.SET_USER_TOPICS,
payload: data.Items,
});
});
};
}
export function handleStateChange(payload) {
return {
type: types.SET_NEW_PREFERENCES,
payload,
};
}
export function handleUpdateTopics({topics, involved}, updateBoth = false) {
return dispatch => {
return requester
.sendPut(endpoints.TOPICS_OF_CONCERN, {
Items: topics,
})
.then(data => {
dispatch({
type: types.SET_USER_TOPICS,
payload: data.Items,
});
if (updateBoth) {
dispatch(handleUpdatePreferences({involved}));
}
});
};
}
export function handleUpdateGetInvoved({involved}) {
return (dispatch, getState) => {
const {auth} = getState();
//prettier-ignore
const url = `${endpoints.v2.INDIVIDUALS}/${auth.user.Key}/preferences`;
return requester
.sendPut(url, {
Items: involved,
})
.then(data => {
const payload = helpers.sortPreferences(data);
dispatch({
type: types.SET_USER_PREFERENCES,
payload,
});
});
};
}
And it's clear I am getting data as undefined with this message:
What is not clear is why? When I do a curl, the data is there:
{"items":[{"category":"None","key":"2883040c-88b8-4899-bd47-114a560d085b","displayText":"Energy
Costs","isSelected":false,"order":1},{"category":"None","key":"a745a3d6-0f64-4595-8734-6082d9c914f7","displayText":"Regulations","isSelected":false,"order":7},{"category":"None","key":"51797a61-8016-4817-a46e-72dee3d8239a","displayText":"Minimum
Wage","isSelected":false,"order":5},{"category":"None","key":"381e24d0-2668-4a69-a993-7d5e1ecaec3b","displayText":"Taxes","isSelected":false,"order":8},{"category":"None","key":"dfaf22cb-111a-46f3-bce3-93fbf4a91490","displayText":"Unemployment
Insurance","isSelected":false,"order":9},{"category":"None","key":"c55b5d2a-a0f3-4c35-bf59-b433259b2059","displayText":"Workers
Compensation","isSelected":false,"order":10},{"category":"None","key":"d4b787d4-550b-4866-a5cc-c6a2de61a91a","displayText":"Healthcare","isSelected":false,"order":4},{"category":"None","key":"c2557854-421d-4b2f-810f-caadf938cded","displayText":"Government
Spending","isSelected":false,"order":3},{"category":"None","key":"cf91f638-c5fa-4252-be01-dce504ae369d","displayText":"Private
Property
Rights","isSelected":false,"order":6},{"category":"None","key":"0eae5ccf-2ba5-41bd-9111-efe7acafa512","displayText":"Finding
Qualified Employees","isSelected":false,"order":2}]}%
In Swagger, I check, the data is there:
{
"items": [
{
"category": "None",
"key": "2883040c-88b8-4899-bd47-114a560d085b",
"displayText": "Energy Costs",
"isSelected": false,
"order": 1
},
{
"category": "None",
"key": "a745a3d6-0f64-4595-8734-6082d9c914f7",
"displayText": "Regulations",
"isSelected": false,
"order": 7
},
{
"category": "None",
"key": "51797a61-8016-4817-a46e-72dee3d8239a",
"displayText": "Minimum Wage",
"isSelected": false,
"order": 5
},
{
"category": "None",
"key": "381e24d0-2668-4a69-a993-7d5e1ecaec3b",
"displayText": "Taxes",
"isSelected": false,
"order": 8
},
{
"category": "None",
"key": "dfaf22cb-111a-46f3-bce3-93fbf4a91490",
"displayText": "Unemployment Insurance",
"isSelected": false,
"order": 9
},
{
"category": "None",
"key": "c55b5d2a-a0f3-4c35-bf59-b433259b2059",
"displayText": "Workers Compensation",
"isSelected": false,
"order": 10
},
{
"category": "None",
"key": "d4b787d4-550b-4866-a5cc-c6a2de61a91a",
"displayText": "Healthcare",
"isSelected": false,
"order": 4
},
{
"category": "None",
"key": "c2557854-421d-4b2f-810f-caadf938cded",
"displayText": "Government Spending",
"isSelected": false,
"order": 3
},
{
"category": "None",
"key": "cf91f638-c5fa-4252-be01-dce504ae369d",
"displayText": "Private Property Rights",
"isSelected": false,
"order": 6
},
{
"category": "None",
"key": "0eae5ccf-2ba5-41bd-9111-efe7acafa512",
"displayText": "Finding Qualified Employees",
"isSelected": false,
"order": 2
}
]
}
I noticed that in the code the items property was written as Items, I tried to change it to items to match the data property, that did nothing.
A colleague suggested the issue could be in the requester object, I do have a question about it too:
import axios from 'axios';
import LocalStorage from './LocalStorage';
import env from 'env';
import * as appcenter from 'utils/appcenterLogger';
import * as titlesHelper from 'utils/titleCaser';
let expired = false;
export const instance = axios.create({
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
NFIBAppID: env.NFIBAppID,
},
});
let preHeaders = {};
async function mergeConfig(config) {
try {
const access = await LocalStorage.get('access');
preHeaders = access;
return {...config, headers: {...access}};
} catch (error) {
return {...config};
}
}
export async function sendGet(url, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.get(url, await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendPost(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.post(url, titlesHelper.lowerCaser(data), await mergeConfig(config))
.then(response => {
console.log(response);
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendPut(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.put(url, titlesHelper.lowerCaser(data), await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendPatch(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.patch(url, data, await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendDelete(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.delete(url, await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export function saveHeaders({data, headers}, timeSent, url) {
try {
if (headers && headers.authorizationtoken) {
LocalStorage.save('access', {
AuthorizationToken: headers.authorizationtoken,
});
}
const timeReceived = new Date();
LocalStorage.save('lastTimeRequestSent', timeReceived);
appcenter.trackRequestTiming(timeSent, timeReceived, headers, url, false);
return titlesHelper.toTitleCase(data);
} catch (_e) {
return false;
}
}
function catchErros({error, timeSent}, url) {
try {
const timeReceived = new Date();
LocalStorage.save('lastTimeRequestSent', timeReceived);
appcenter.trackRequestTiming(timeSent, timeReceived, error, url, true);
if (error && error.response) {
saveHeaders({
headers: preHeaders,
});
const {data} = error.response;
const message = data.message || data.Message;
if (message.includes('TokenExpired')) {
expired = true;
}
}
return Promise.reject(titlesHelper.toTitleCase(error.response.data));
} catch (_e) {
return error;
}
}
export function resetTokenExpired() {
expired = false;
}
I am seeing Promise syntax being mixed with async/await syntax, could this be causing an issue?
I tried to see if perhaps the issue was with the authorization token, so I console logged it:
let preHeaders = {};
async function mergeConfig(config) {
try {
const access = await LocalStorage.get('access');
console.log(access);
preHeaders = access;
return {...config, headers: {...access}};
} catch (error) {
return {...config};
}
}
but I am successfully getting that back:
{AuthorizationToken: "<bunch-o-numbers>"}
What I know at this point is that without the logic inside of the saveHeaders() function, a registered users' password will return undefined.
To complicate things, this application uses action helpers, which I have never implemented, but I see that Items property in there all over the place, keep in mind that the original warning read Items, but I changed it everywhere to items to make it match the JSON item property in the hope that would be the fix.
However, I have now come across these action helper files with the following code, action-helpers.js:
import * as endpoints from 'endpoints';
import * as requester from 'services/Requester';
import compareDesc from 'date-fns/compare_desc';
export async function fetchTransaction() {
try {
const response = await requester.sendGet(endpoints.TRANSACTIONS);
const {Items = []} = response;
return Items.sort((a, b) => compareDesc(a.DateTime, b.DateTime));
} catch (error) {
return [];
}
}
and in data-helpers.js:
export function sortPreferences(data) {
const sorted = data.Items.sort((a, b) => a.Order - b.Order);
const communications = sorted.filter(
p => p.Category === 'CommunicationPreferences'
);
const privacy = sorted.filter(p => p.Category === 'MemberPrivacy');
const involved = sorted.filter(p => p.Category === 'GetInvolved');
const format = data.EmailFormatType === 'HTML' ? 'HTML' : 'Plain Text';
return {
communications,
privacy,
involved,
emailFormatType: format,
isEmailAllowed: data.IsEmailAllowed,
isPhoneAllowed: data.IsPhoneAllowed,
};
}
Most probably you are not getting the expected response from your requester function.
Try logging the response from the requester and see the output. You might have to use response.json() to resolve the promise correctly. This is assuming your requester class/function works like that.
I'm a newbie in NuxtJs. I'm trying to implement an external API Call with axios which I get token and store it on cookie. Everything works well in development. But when I try to run npm run generate it gives me errors that I don't know what to do.
When I delete nuxtSeverInit, npm run generate runs smoothly. And after some research, i think that nuxtServerInit that I'm using shouldn't be used. Can anyone please tell me how to make it work.
This is the first project in a new company, so I'm trying to prove myself. Please help me with it. Will you.
Click here for image that shows the error that appears after npm run generate
This is store/index.js file
import Vuex from 'vuex'
var cookieparser = require('cookieparser')
const createStore = () => {
return new Vuex.Store({
state: {
auth: null,
},
mutations: {
update (state, data) {
state.auth = data
}
},
actions: {
nuxtServerInit ({ commit }, { req }) {
let accessToken = null
if (req.headers.cookie) {
var parsed = cookieparser.parse(req.headers.cookie)
if(parsed){
accessToken = parsed.auth
}
}
commit('update', accessToken)
},
}
})
}
export default createStore
middleware/authenticated.js file
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.auth) {
return redirect('/login')
}
}
middleware/notAuthenticated.js file
export default function ({ store, redirect }) {
// If the user is authenticated redirect to home page
if (store.state.auth) {
return redirect('/app/dashboard')
}
}
login.vue file
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
this.button_title = 'One moment ...';
let submitted_user_data = {
'username': this.emailAddress,
'client_id': this.user_uuid,
'password': this.password,
}
MerchantServices.do_user_login(submitted_user_data)
.then(response => {
let access_token = response.data.access_token;
this.postLogin(access_token);
})
.catch(error => {
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
});
return;
}
});
},
postLogin: function(access_token_val) {
if(access_token_val != ''){
setTimeout(() => {
const auth = {
accessToken: access_token_val
}
this.$store.commit('update', auth)
Cookie.set('auth', auth)
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
this.$router.push('/app/dashboard')
}, 1000)
}else{
alert('hello')
}
},
and the last user login api call which also returns the token.
do_user_login(user){
var user_details = 'username='+user.username+'&client_id='+ user.client_id +'&grant_type=password&password='+user.password+''
return axios.post('myapiurl', user_details )
.then(response => {
return response;
});
},
Acording to Nuxt Docs req is not available on nuxt generate.
You should use nuxt build and than nuxt start after that.