How can I pass body params in a Next.js rewrite? - authentication

I have this POST request:
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: JSON.stringify({
grant_type: "password",
client_id: "client",
client_secret: "clientsecret",
username: "matias#gmail.com",
password: "12341234"
})
};
const token = fetch("http://localhost:3000/connect/token", requestOptions)
.then(response => console.log(response))
.catch(error => console.log(error));
I would like to rewrite it so I can bypass CORS error in development environment. To achieve this, I have this code in my next.config.js file:
const nextConfig = {
reactStrictMode: false,
async rewrites() {
return process.env.NODE_ENV === "development"
? [
{
source: "/connect/token",
destination: "http://localhost:4446/connect/token",
}
]
: [];
},
}
How can I specify the body params (grant_type, client_id, client_secret, username and password) to this Next.js rewrite? The error I am getting suggest these values are not present in my rewrite:
fail: IdentityServer4.Validation.ClientSecretValidator[0]
No client identifier found

I don't know about Next.js, but have you tried to add the url to the client configuration as Allowed Cors Origins?
You could add in your develop client the url http://localhost:3000.

Related

Nuxt auth module axios not setting CSRF token on request

I'm using the Nuxt auth module v5 and the Laravel sanctum provider. My csrf-cookie route works fine, and my login route works fine, but when trying to call this.$axios from a function, such as when creating a user's account (since auth module doesn't offer this) I'm getting a CSRF token mismatch.
It would appear that using axios directly like this doesn't have access to setting the cookie since no user logged in, how can I get the cookie to be set?
Method for account creation
/*
** Create accounr
*/
createAccount () {
this.feedback.isShown = false
this.isCreatingAccount = true
if (this.apiAccountCreationSource) this.apiAccountCreationSource.cancel('aborted')
const CancelToken = this.$axios.CancelToken
this.apiAccountCreationSource = CancelToken.source()
this.$axios.post(`${this.$config.apiUrl}/api/account`, this.account, {
cancelToken: this.apiAccountCreationSource.token,
timeout: 30 * 1000
}).then(res => {
this.apiAccountCreationSource = null
this.setContextualResponse(res)
setTimeout(() => {
this.login()
}, 250)
}).catch(err => {
this.setContextualResponse(err.response ? err.response.data : null)
}).finally(() => {
this.isCreatingAccount = false
})
},
Nuxt config
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
credentials: true,
baseURL: process.env.API_DOMAIN
},
// Auth module configuration: https://auth.nuxtjs.org/
auth: {
redirect: {
login: '/account/login/',
logout: '/account/login/',
callback: '/account/login/',
home: '/account/dashboard/'
},
strategies: {
'laravelSanctum': {
provider: 'laravel/sanctum',
url: process.env.API_DOMAIN,
endpoints: {
login: { url: '/api/login', method: 'post' },
logout: { url: '/api/account/logout', method: 'post' },
user: { url: '/api/account', method: 'get', propertyName: 'user' }
}
}
}
},
If you need to get the CSRF token all you need to do is make a request to your token endpoint and your browser should save the XSRF token. Then axios will automatically send this token in every subsequent request.
So all that you need to do is make a axios GET request to your csrf-cookie route before you send your POST request.
this.$axios.get(`${this.$config.apiUrl}/sanctum/csrf-cookie`)
Or you can chain both requests doing something like this:
this.$axios.get(`${this.$config.apiUrl}/sanctum/csrf-cookie`).then(() => {
return this.$axios.post(`${this.$config.apiUrl}/api/account`, this.account, {
cancelToken: this.apiAccountCreationSource.token,
timeout: 30 * 1000
}).then((res) => {
this.apiAccountCreationSource = null
this.setContextualResponse(res)
setTimeout(() => {
this.login()
}, 250)
}).catch((err) => {
this.setContextualResponse(err.response ? err.response.data : null)
}).finally(() => {
this.isCreatingAccount = false
})
})
Your authentication strategy works without this hassle because it handles this csrf request internally (https://github.com/nuxt-community/auth-module/blob/dev/src/providers/laravel-sanctum.ts)
References:
https://laravel.com/docs/8.x/sanctum#csrf-protection
https://github.com/axios/axios/issues/708#issuecomment-280920224

Adding basic auth to all requests in Cypress

I'm a Cypress newbie and need to add basic auth to all cy.visit() calls.
The auth credentials are dependent on the deployment (i.e. they are specific to the 'baseUrl' which we set in the environment config files).
Currently, I have;
cy.visit("/", {
auth: {
username: '...',
password: '...'
}
});
What I want is to move the 'auth' object to the evg config files so I only need cy.visit("/") in the specs.
Many thanks
If you plan to reuse the authentification then is better to create a separate method for authentication e.g.:
1. Create a new custom command in `cypress/support/commands.js,
since it is loaded before any test files are evaluated via an import statement in your supportFile (cypress/support/index.js by default).
Cypress.Commands.add('login', () => {
// (you can use the authentification via API request)
return cy
.request({
method: 'POST',
url: your_url,
form: true,
body: {
username: Cypress.env('username'),
password: Cypress.env('password'),
grant_type: 'password',
client_id: your_clientId,
client_secret: your_clientSecret,
scope: 'openid',
},
})
})
2. Then use it in your test:
describe('My Test Name', () => {
before(() => {
cy.login();
});
it('should visit my base URL', () => {
cy.visit('/');
});
});
Note 1: Check how to set the environment variables here: Cypress.io: Environments Variables
Note 2: Check how to use the custom commands here: Custom Commands - Correct Usage
EDIT: since your syntax is correct - I will just share a way I use to do it in my tasks.
If your auth is working correctly you can make custom command - visitAndAuthorise like this for example:
Cypress.Commands.add("shopAdmin_visitAndLogin", (url) => {
cy.log('shopAdmin_visitAndLogin')
cy.visit(url)
cy.get('[data-qa="emailInput"]')
.type(Cypress.env('credentials').email)
cy.get('[data-qa="passwordInput"]')
.type(Cypress.env('credentials').password)
cy.get('[data-qa="loginButton"]')
.click()
cy.get('[data-qa="logOutButton"]')
.should('be.visible')
})
And your cypress.env.json file would need to include an object for the credentials like this:
{
"credentials": {
"email": "myEmail#gmail.com",
"password": "myPassword"
}
}
Or following your syntax:
Cypress.Commands.add("shopAdmin_visitAndLogin", (url) => {
cy.log('shopAdmin_visitAndLogin')
cy.visit(url, {
auth: {
username: Cypress.env('credentials').username,
password: Cypress.env('credentials').password
}})
})
If you have HTTP basic auth for all pages add this code to your cypress/support/commands.js:
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
options = options || {}
options.auth = {
username: 'replace_this_with_the_username',
password: 'replace_this_with_the_password'
}
return originalFn(url, options);
});
This is how I handled Basic Auth with Cypress using cy.request:
cy.request({
method:'POST',
url:'myURL',
body: {
Name: name,
userId: userId,
languageId: languageId
},
headers: {
authorization: 'Basic lasdkfjlsdyZHRoYXRpa25vdzp'
},
}).then(response => {
expect(response.status).to.equal(201)
})
})
Basically, the "headers" object inside the cy.request do the magic.

Axios formData with image is sending empty array

I have a put method to my profile route in the backend, I need a token authentication so in order to be authorized I have to pass the token through a header, I get an error because for some reason it's sending an empty formData when I log the request in my backend.
I tested the backend with postman and everything works as intended so it's not a backend issue, it's totally something wrong in the request I'm doing from the frontend, but I don't know how to handle this, any clue?
profile_update() {
let params = {
email: this.profile.email,
password: this.profile.password,
coin: this.profile.coin,
phone_country: this.profile.phone_country,
phone_area: this.profile.area_code,
phone_number: this.profile.number,
first_name: this.profile.first_name,
last_name: this.profile.last_name,
date_of_birth: this.profile.date_of_birth,
gender: this.profile.gender,
city_id: this.profile.city,
wants_to_change_password: this.profile.wants_to_change_password,
state_id: this.profile.state,
city_id: this.profile.city
}
let headers = {
'Authorization': 'Bearer' + this.token
}
let formData = new FormData();
formData.append('profile-picture', this.profile.profile_picture)
formData.append('data', params)
formData.append('headers', headers)
formData.append('_method', 'PUT')
axios.put(`http://127.0.0.1:8000/api/profile`, formData, headers).then(res => {
console.log(res)
}).catch(e => {
console.log(e)
})
}
Try this way
axios.put(`http://127.0.0.1:8000/api/profile`, {'profile-picture':this.profile.profile_picture}, {
headers: {
Authorization: 'Bearer ' + this.token,
'content-type': 'multipart/form-data'
}
}).then(res => {
console.log(res)
}).catch(e => {
console.log(e)
})
Hope this may solve your problem. For more info read docs

XCRF Token Axios / Nuxt plugin - 403 Forbidden

I've been searching for solution since few hours and not able to find any.
Doing post request via axios nuxt plugin is not working as expected:
nuxt.config.js file:
axios: {
debug: true,
baseURL: `${process.env.API_PROTOCOL}://${process.env.API_HOST}${process.env.API_PORT ? `:${process.env.API_PORT}` : ''}${process.env.API_PREFIX}`,
},
axios plugin:
export default function ({
$axios, redirect, store,
}) {
$axios.setHeader('Content-Type', 'application/json');
$axios.setHeader('Accept', 'application/json');
$axios.onRequest((config) => {
const configLocal = config;
const { jwt } = store.state.authentication;
if (jwt) {
configLocal.headers.JWTAuthorization = `Bearer ${jwt}`;
}
if (config.method === 'post') {
configLocal.headers['X-Requested-With'] = 'XMLHttpRequest';
configLocal.headers['X-XSRF-TOKEN'] = store.state.authentication.crfToken;
}
});
}
And call methods:
authenticateUser({ commit }, { data }) {
return this.app.$axios.$post('auth/login', data).then(({ token }) => {
this.$cookies.set('jwt', token);
commit('setAction', { key: 'jwt', value: token });
}).catch(e => console.log(e));
},
getCRFToken({ commit }) {
return this.app.$axios.$get('auth/token').then(({ token }) => {
this.$cookies.set('crf', token);
commit('setAction', { key: 'crfToken', value: token });
});
},
The getCRFTToken works like a charm by returning CSRF token:
Request URL: http://127.0.0.1:8080/auth/token
Request Method: GET
Status Code: 200
Remote Address: 127.0.0.1:8080
Referrer Policy: no-referrer-when-downgrade
{"token":"92618f1e-0ed3-472b-b6a9-db2201a02d86"}
But whenever I do login...
It fails. Was digging in github - trying to set X-XSRF-TOKEN header in many places, but nope - still doesn't work. Anyone know the solution for this case ?
Edit
In the config folder there is shield.js file which config is blocking your route.
Set enable in csrf to false in the file.
It will start woking then.

Express CORS response

I am using express to return an api response retrieved via a request call.
router.post('/', function(req, res){
var options = {
...
}
rp(options)
.then(function (parsedBody) {
console.log(parsedBody);
res.json(parsedBody)
})
The console log displays the expected payload, also when I use Postman I also see the expected payload.
When though my client app gets the response it is:
Response {type: "cors", url: "http://localhost:3001/api/", redirected: false, status: 200, ok: true, …}
Ive tried adding CORS middleware:
app.use(function(request, response, next) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers",
"Origin, X-Rquested-With, Content-Type, Accept");
next();
});
My client is a very simple react app using fetch:
fetch('http://localhost:3001/api/', {
method: 'POST',
dataType: 'JSON',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: data,
}).then((response) => {
console.log(response);
}).catch((response) => {
console.log('Error');
});
My react app is localhost:3000 and the express api localhost:3001, the expected payload is a very simple object...
{
athlete: {
firstname: "Matt"
...
}
}
How can I just forward on the api request response to the clients fetch success method?
The problem is not CORS related, the response within the react app needed parsing as json:
.then((response) => {
console.log(response.json());
})