I have a trouble with Ember Simple Auth.
I'm trying to connect my server-side application, which working on Django 1.9 with DRF, and client-side which working on Ember 2.2.
On server side I'm obtaining token on 'http://localhost:8000/api-token-auth/'. Function requires two args from request: "username" and "password". But Ember Simple Auth send POST request with args: "username[identification]" and "password[password]", and server returns "400". I think that problem with arguments keys.
POST request
Responce
I tried to change .authenticate method in oauth2-password-grant.js(i can't write custom authenticator because i'm newbee in javascript), but nothing changed.
Manually POST request returns expected answer.
Please tell me the way to solve this problem.
And please forgive me for my english.
authenticate(identification, password, scope = []) {
return new RSVP.Promise((resolve, reject) => {
const data = { 'grant_type': 'password', username: identification, password };
const serverTokenEndpoint = this.get('serverTokenEndpoint');
const scopesString = Ember.makeArray(scope).join(' ');
if (!Ember.isEmpty(scopesString)) {
data.scope = scopesString;
}
this.makeRequest(serverTokenEndpoint, data).then((response) => {
run(() => {
const expiresAt = this._absolutizeExpirationTime(response['expires_in']);
this._scheduleAccessTokenRefresh(response['expires_in'], expiresAt, response['refresh_token']);
if (!isEmpty(expiresAt)) {
response = Ember.merge(response, { 'expires_at': expiresAt });
}
resolve(response);
});
}, (xhr) => {
run(null, reject, xhr.responseJSON || xhr.responseText);
});
});
},
My variant:
const data = { 'grant_type': 'password', 'username': identification, 'password': password };
authenticate: function () {
// var username = this.getProperties('username');
// var password = this.getProperties('password');
const {username, password} = this.getProperties('username', 'password');
this.get('session').authenticate('authenticator:oauth2', username, password).catch((reason) => {
this.set('errorMessage', reason.error || reason);
});
}
It was my mistake.
Related
I'm working on an e-commerce web app using Laravel and Vuejs. I chose Stripe's API to accept and manage payments.
In my Vue component, which contains the payment form, and before making it visible(I'm using a multi-step form), I send a post request to my payments store controller function to 'initialize' Stripe and get the clientSecret variable. As follows:
axios
.post('http://127.0.0.1:8000/payments', {
headers: {
'content-type': 'multipart/form-data'
},
'total': this.total,
'mode_payment': this.mode_payment
})
This is how the store function looks like in my PaymentController:
public function store(Request $request)
{
$payment = new Payment;
$payment->montant_payment = $request->total;
$payment->mode = $request->mode_payment;
$payment->date_payment = Carbon::now();
$payment->statut_payment = false;
$payment->save();
Stripe::setApiKey(env('STRIPE_SECRET'));
header('Content-Type: application/json');
$intent = PaymentIntent::create([
'amount' => $payment->montant_payment,
'currency' => 'usd',
]);
$clientSecret = Arr::get($intent, 'client_secret');
$amount = $payment->montant_payment;
return response()->json($clientSecret);
}
As you can see, the store function sends back a JSON response containing the clientSecret variable. This response is then captured by the same Vue component discussed above.
This is how the Vue component captures the response:
.then((response) => {
this.clientSecret = response.data;
var stripe =
Stripe('pk_test_51JL3DSLJetNHxQ3207t13nuwhCB1KPvUJJshapsOQATnZn79vA4wK3p9Hf2yi2uUXfXXWdAtLZGRepfJGvRnd7oI006Kw6rFTC');
document.querySelector("button").disabled = true;
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
}
};
this.card = elements.create("card", { style: style });
this.card.mount("#card-element");
this.card.on('change', ({error}) => {
let displayError = document.getElementById('card-error');
if (error) {
displayError.classList.add('alert', 'alert-warning');
displayError.textContent = error.message;
} else {
displayError.classList.remove('alert', 'alert-warning');
displayError.textContent = '';
}
});
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(ev) {
ev.preventDefault();
// If the client secret was rendered server-side as a data-secret attribute
// on the <form> element, you can retrieve it here by calling `form.dataset.secret`
stripe.confirmCardPayment(String(this.clientSecret), {
payment_method: {
card: this.card,
billing_details: {
name: "testan testo"
}
}
})
.then(function(result) {
if (result.error) {
// Show error to your customer (e.g., insufficient funds)
console.log(result.error.message);
}
else {
// The payment has been processed!
if (result.paymentIntent.status === 'succeeded') {
console.log(result.paymentIntent);
window.location.href = 'http://127.0.0.1:8000/payment-success/';
}
}
});
});
})
Using Vue web dev tools I checked that the clientSecret variable has been successfully passed from the laravel store controller function to the payment Vue component, thanks to the execution of the commmand : this.clientSecret = response.data;.
However, when clicking the pay button, I get the following error in my console:
Uncaught IntegrationError: Missing value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id}_secret_${secret}.
at X ((index):1)
at Q ((index):1)
at lo ((index):1)
at (index):1
at (index):1
at e.<anonymous> ((index):1)
at e.confirmCardPayment ((index):1)
at HTMLFormElement.eval (ComLivPay.vue?5598:339)
I guess the problem then is in the next portion of code:
stripe.confirmCardPayment(String(this.clientSecret), {
payment_method: {
card: this.card,
billing_details: {
name: "testan testo"
}
}
})
EDIT:
Since the problem seems to arise from the string form of the this.clientSecret variable. Here's how it looks like in Vue dev tools:
I'm trying to make a cypress api call and get the value to be use on a next stage api call and when i make a return it just send me a big obj of commands
the call im making is
Cypress.Commands.add('getSession', () => {
cy.request({
method: 'POST',
url: `${Cypress.env('apiURL')}/New`,
headers: {
'Client-Type': 'backend'
}
})
.its('body')
.then(json => {
return {
id: json.value.props.id,
name: json.value.props.name
}
})
})
Cypress.Commands.add('createNew', (email = Cypress.env('userEmail'), password = Cypress.env('userPassword')) => {
const session = cy.getSession()
cy.log('api respond', session.id)
cy.createMember(email, password, session.id)
})
and the response im getting is
[$Chainer] with a big obj
I'm not sure if the return on .then put it on a container but i can't find the value any when if someone can suggest what i have made wrong on the request call, that will be helpful
From the cypress docs:
So the createNew command must be rewritten to respect the async nature of cy commands.
Using then
Cypress.Commands.add('createNew', (email = Cypress.env('userEmail'), password = Cypress.env('userPassword')) => {
cy.getSession().then( session => {
cy.log('api respond', session.id)
cy.createMember(email, password, session.id)
})
})
Using aliases
Cypress.Commands.add('createNew', (email = Cypress.env('userEmail'), password = Cypress.env('userPassword')) => {
cy.getSession().as("session")
cy.get("#session").then(session => {
cy.log('api respond', session.id)
})
cy.get("#session").then(session => {
cy.createMember(email, password, session.id)
})
Hi developers I have currently problem this project is an old project where the developer created. right now, we need to adjust some of there functionality. Right now I already extract the files to my localhost folder and now work without error on run watch and artisan serve. So the problem here is on login on the console it shows that http://localhost:8000/project/oauth/token 405 (Method Not Allowed), I really don't understand why this shows on the localhost however on the live server it works.
This project created using Vue Js and Laravel for the backend.
I will show you guys the authentication function.
Login Function:
authenticate(){
this.login_alert = false
this.$validator.validateAll().then((result)=>{
if(result){
const self = this;
const authUser = {}
try{
const data = {
username: this.email,
password: this.password,
remember: this.remember_me,
client_id: '2',
client_secret: 'secret',
grant_type : 'password',
scope : ''
}
this.$store.dispatch('AUTH_REQUEST',data)
.then(response=>{
console.log(data);
authUser.access_token = response.access_token
authUser.refresh_token = response.refresh_token
authUser.expires_in = response.expires_in
window.localStorage.setItem('project_token',JSON.stringify(authUser))
/*LOGIN*/
this.login_alert = false
this.loading = false
window.location.reload()
})
.catch(error=>{
this.login_alert = true
window.localStorage.removeItem('project_token')
this.loading = false
})
}catch(err){
console.log(err);
}
}
})
}
For the AUTH REQUEST:
AUTH_REQUEST:({commit,dispatch},obj)=>{
return new Promise((resolve,reject) => {
axios({
url: '/project/oauth/token',
data: obj,
method:'post',
config:'JSON'
})
.then(response=>{
if(response.status == 200){
resolve(response.data);
}
})
.catch(error=>{
reject(error);
localStorage.removeItem('project_token');
commit('AUTH_ERROR',error);
})
})
},
Hope some one experience this. thanks.
In my case, the compilation of the route should be specified properly, for example
async purchaseDelete(purchase) {
Csrf.getCookie();
return Api.delete('/api/purchase_settings/${purchase.id}');
},
the single tick on the axios delete method didnt represent correctly
async purchaseDelete(purchase) {
Csrf.getCookie();
return Api.delete(`/api/purchase_settings/${purchase.id}`);
}
When changed to back ticks, it responded with the correct result
I used the following code to change the password, but I get "Request failed with status code 400". Can someone give me an indication of where the problem is?
axios.post ('http: // localhost: 1337 / auth / reset-password', {
code: '',
password: '1234567',
passwordConfirmation: '1234567',
}
, {
headers: {
Authorization: `Bearer $ {this.currentUser.jwt}`
}
}
) .then (response => {
// Handle success.
console.log ('Your user \' s password has been changed. ');
})
.catch (error => {
// Handle error.
console.log ('An error occurred:', error);
});
}
Thanks in advance
Another alternative way is by using a password reset controller. The scenario is by POST a password object to http://localhost:1337/password, the controller will validate the current password then update the password with given newPassword, and return a new jwt token.
We will post a password object as follows:
{
"identifier": "yohanes",
"password": "123456789",
"newPassword": "123456",
"confirmPassword": "123456"
}
The steps are:
Create password reset route /api/password/config/routes.json:
{
"routes": [
{
"method": "POST",
"path": "/password",
"handler": "password.index"
}
]
}
Create password reset controller at /api/password/controllers/password.js
module.exports = {
index: async ctx => {
return 'Hello World!';
}
}
Note: Don't forget to enable password index at Roles -> Permission -> Application.
Point Postman to http://localhost:1337/password. The response will display the text Hello World!.
Update the password controller:
module.exports = {
index: async ctx => {
// Get posted params
// const params = JSON.parse(ctx.request.body); //if post raw object using Postman
const params = ctx.request.body;
// The identifier is required.
if (!params.identifier) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.provide',
message: 'Please provide your username or your e-mail.',
})
);
}
// Other params validation
.
.
.
// Get User based on identifier
const user = await strapi.query('user', 'users-permissions').findOne({username: params.identifier});
// Validate given password against user query result password
const validPassword = await strapi.plugins['users-permissions'].services.user.validatePassword(params.password, user.password);
if (!validPassword) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.invalid',
message: 'Identifier or password invalid.',
})
);
} else {
// Generate new hash password
const password = await strapi.plugins['users-permissions'].services.user.hashPassword({password: params.newPassword});
// Update user password
await strapi
.query('user', 'users-permissions')
.update({ id: user.id }, { resetPasswordToken: null, password });
// Return new jwt token
ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue({ id: user.id }),
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, { model: strapi.query('user', 'users-permissions').model }),
});
}
}
}
Once the password object posted, the controller will update the user password and return a newly created jwt token.
The complete code can be found here. Tested on Strapi v.3.3.2
You will have to use the PUT /users/:id route (from the User API)
If you want this route used by a user, you will have to create a isOwner policy and apply it to this route.
To let only the current user udpate it's own password and not all users password.
Here some documentation:
Create a policy
Get the current user in the request
Customize the User plugin
Here is yohanes's solution adapted to Strapi v4
For some reason the Strapi team has removed the hashPassword method of the users-permission.user service, so we need to generate the hash ourselves now. For this we use the same having method as v3 did. We need to import bcrypt like this: const bcrypt = require("bcryptjs");
Out new changePassword needs to look something like this:
async changePassword(ctx) {
const userId = ctx.request.body.userId;
const currentPassword = ctx.request.body.currentPassword;
const newPassword = ctx.request.body.newPassword;
if (!userId || !currentPassword || !newPassword) {
return ctx.throw(400, "provide-userId-currentPassword-newPassword");
}
let user = await strapi
.query("plugin::users-permissions.user")
.findOne({ id: userId });
const validPassword = await strapi
.service("plugin::users-permissions.user")
.validatePassword(currentPassword, user.password);
if (!validPassword) {
return ctx.throw(401, "wrong-current-password");
} else {
// Generate new hashed password
const password = bcrypt.hashSync(newPassword, 10);
user = await strapi.query("plugin::users-permissions.user").update({
where: { id: user.id },
data: { resetPasswordToken: null, password },
});
// Return new jwt token
ctx.send({
jwt: strapi.service("plugin::users-permissions.jwt").issue({
id: user.id,
}),
user: sanitizeOutput(user),
});
}
},
im having this issue where i send a request to the API to retrieve all users, the login function is called(index.vue) when called it tries to go to api/users/all which in this case should return all the users in that collection.
using Postman the API returns the correct results and if i console.log the output in the routeUsers before i send the response back, it outputs all the correct data to the console
when it returns to index.vue, the response status code is 0.
ive had a look online and some things are mentioning about CORS Headers but i dont think thats applicable to me and other things about the response has been cancelled,
can anyone shed some light on this for me and help me try to fix it?!
API main.js
var app = express();
var users = require('./routes/routeUsers');
app.use('/users', users);
module.exports = app;
api/models/users.js
var db = require('../Utilities/db')
module.exports.all = function(cb) {
var collection = db.get().collection('users')
collection.find().toArray(function(err, docs) {
cb(err, docs)
})
}
api/routes/routeUsers.js
var express = require('express')
, router = express.Router()
var user = require('../models/users');
router.get('/all', function(req, res) {
user.all(function(err, users) {
res.send(users);
})
})
Index.vue
export default {
data: function () {
return {
username: '',
password: '',
users: []
}
},
methods: {
login: function() {
Vue.http.get('/api/users/all').then((response) => {
console.log("SUCCESS",response);
this.users = response.body;
console.log(users);
}, function (error) {
console.log("Error", error.status); // handle error
});
}
}
};
The issue was that the inputs were in a form tag. removed Form tag and worked fine.