I try to create auth on my site via battle.net. It's my first experience in OAuth. There is documentation https://develop.battle.net/documentation/api-reference/oauth-api
Now I can get code and want to get access token by code. I provide credentials. Using Koa.js.
const { code } = ctx.query;
const redirect_uri = 'http://127.0.0.1:3000/auth';
const client_id = '0010d...25f44b31...5c34b12f4af7'
const secret = 'MY_VERY_SECRET_CODE';
const scope = 'wow.profile';
if( !code ) ctx.redirect(`https://us.battle.net/oauth/authorize?response_type=code&client_id=${client_id}&redirect_uri=${redirect_uri}&scope=${scope}`);
try {
const { data } = await axios.post('https://us.battle.net/oauth/token', {
grant_type: 'authorization_code',
code,
redirect_uri,
client_id
}, {
auth: {
username: client_id,
password: secret
}
})
console.log(data);
} catch (e) {
console.error(e)
}
ctx.body = {
message: 'OK',
}
Redirect works and I got code. But how I should build a query with the gotten code? But I got error
data:
{ error: 'invalid_request',
error_description: 'Missing grant type' } },
I should use form data type.
formData.append('grant_type', 'authorization_code');
formData.append('code', code);
formData.append('redirect_uri', redirect_uri);
formData.append('client_id', client_id);
const { data: authData } = await axios.post('https://eu.battle.net/oauth/token',
formData,
{
auth: {
username: client_id,
password: secret,
},
headers: {
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
}
},
)
Related
I am working on an existing project that was created by someone in Vue.js and Symfony. Right now I am trying to integrate "the login" module but it is not working. Specifically, I cannot login. In the Login.vue file there is an API calling for login /api/login.
Here is current code:
async Login() {
const data = { username: this.email, password: this.password };
this.loading = true;
const response = await fetch(
"/api/login",
{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
}
);
console.log('333', response)
let newtoken = response.token
this.$store.dispatch("loginSuccess", response.token);
await this.getMyProfile(newtoken);
if (this.$route.query.r) {
await this.$router.push({ path: this.$route.query.r });
//window.location.href = this.$route.query.r;
} else {
await this.$router.push({ path: "/center" });
// window.location.href = "/center";
}
},
But I think the API is not working. Should I set any base_url in any file? Here is my .env file. Where I am wrong?
DATABASE_URL="mysql://root:#127.0.0.1/dating_sugar_sta?serverVersion=mariadb-10.4.25&charset=utf8"
Created a login method but it spits out the token regardless of what is entered in the Userfields
This uses swagger API and I'm trying to develop the frontend and backend
I'm relatively new to nodejs/javascript
Any Help would be appreciated!
login.js
var form = document.getElementById('login')
form.addEventListener('submit', login)
async function login(event) {
event.preventDefault()
const username = document.getElementById('username').value
const password = document.getElementById('password').value
const result = await fetch('/v1/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username,
password
})
}).then((res) => res.json())
if (result.status === 'ok') {
// everythign went fine
console.log('Got the token: ', result.data)
localStorage.setItem('token', result.data)
alert('Login Successful')
return false;
} else {
alert(result.error)
}
}
userController.JS
login: async (req,res)=> {
const { userName, password } = req.body
const existUsername = await userModel.findOne({ userName: req.body.userName, password: req.body.password}).then((existUsername) =>{
if (existUsername){
res.status(400).json({status: 'Failed', message: `User was Not found`, data: null})
return;
}
try{
async() => {
await bcrypt.compare(password, req.body.password) }
// the username, password combination is successful
const token = jwt.sign(
{
id: userModel._id,
userName: userModel.userName
},
JWT_SECRET
)
res.json({ status: 'ok', data: token })
}
catch (e) {
res.status(400).json({status: 'Failed', message: `${e.message}`, data: null})
}
});
},
I'm student in web development. Currently, I'm trying to build a basic project, where I'm stack in implementing reset password feature, I really need help in how fetching reset password API in front-end using Axios. In short, the reset password API that I implemented works fine on Postman, but whenever I tried to pass in front-end and fetch the API in order to enable users to enter their new password and passwordValidation I kinda lost, below I share my code snippets:
backend code reset password
resetPassword = async(req, res) => {
try {
// Step 1: Get user based on the token
const validateHashedToken = crypto
.createHash('sha256')
.update(req.params.token)
.digest('hex');
const user = await User.findOne(
{
passwordResetToken: validateHashedToken,
passwordResetExpires: { $gt: Date.now() }
});
user.password = req.body.password;
user.passwordValidation = req.body.passwordValidation;
user.passwordResetToken = undefined;
user.passwordResetExpires = undefined;
await user.save();
// Step 3: Update the "passwordChangedAt" date
// Step 4: Log the user in and send a JWT
genResJWT(user, 200, res);
} catch (error) {
console.log('error', error)
}
};
Routes:
router
.route('/api/v1/users/resetpassword/:token')
.get(viewsController.getResetPasswordUrl)
.patch(viewsController.resetpassword);
controllers
exports.getResetPasswordUrl = async(req, res) => {
try {
const { token } = req.params.token;
const validToken = await User.findOne(
{
passwordResetToken: token
}
);
res.status(200).render('resetPassword',
{
title: 'resetpassword',
token: validToken
});
} catch (error) {
console.log(error);
}
};
exports.resetpassword = (req, res) => {
// I'm stack here and I really need help
res.status(200).render('profile', {
title: 'reset password successfuly'
});
};
front-end fetching api code:
import axios from 'axios';
export const resetPassword = async (password, passwordValidation) => {
try {
const res = await axios({
method: 'PATCH',
url:
`http://127.0.0.1:3000/api/v1/users/resetpassword/:token`,
data: {
password,
passwordValidation
}
});
if (res.data.status === 'success') {
window.setTimeout(() => {
location.replace('/me');
}, 500);
}
} catch (error) {
console.log('error', error.response.data.message);
}
};
On the front end, you are making a request to http://127.0.0.1:3000/api/v1/users/resetpassword/:token. Since token is a route parameter, you are directly passing in the string ":token" and not the actual value of the token.
Try this instead:
const res = await axios({
method: 'PATCH',
url:
`http://127.0.0.1:3000/api/v1/users/resetpassword/${token}`,
data: {
password,
passwordValidation
}
});
where token is a variable you need to define.
Assuming that you are using express, here is some documentation about parameter routing: https://expressjs.com/en/guide/routing.html#route-parameters
I fixed my issue with the following steps:
1- Use only GET request in my '/resetpassword/:token' route and submit the PATCH request with Axios.
2- Pass the 'token' along with the 'password' and the 'passwordValidation' as input data in the PATCH request.
3- create a hidden input within the 'resetPassword' form in order to submit the 'token' with the password and the 'passwordValidation' whenever users confirm their updated password.
Below is my code snippet in order to explain how goes the solution:
Routes:
router.get(
'/resetpassword/:token',
viewsController.resetPassword
)
controllers
exports.resetPassword = (req, res) => {
const token = req.params.token;
res.status(200).render('/login', {
title: 'reset password successfuly', { token }
});
};
front-end fetching API code:
import axios from 'axios';
export const resetPassword = async (password, passwordValidation, token) => {
try {
const res = await axios({
method: 'PATCH',
url:
`/api/v1/users/resetpassword/${token}`,
data: {
password,
passwordValidation
}
});
if (res.data.status === 'success') {
window.setTimeout(() => {
location.assign('/login');
}, 1000);
}
} catch (error) {
console.log('error', error.response.data.message);
}
};
the resetPassword form:
extends goaheadtravel
block content
main.main
.resetpassword-form
h2.heading-secondary.ma-bt-lg Please enter a new password and validate it
form.form.resetpassword--form
.form__group.ma-bt-md
label.form__label(for='password') Password
input#password.form__input(type='password' placeholder='••••••••' required='' minlength='8')
.form__group.ma-bt-md
label.form__label(for='passwordValidation') Confirm password
input#passwordValidation.form__input(type='password' placeholder='••••••••' required='' minlength='8')
input#resetToken(type='hidden' value=`${token}`)
.form__group.right
button.btn.btn--green Confirm new password
Hope that my solution will help other developers!
I am using Next-Auth Credentials provider to authenticate using our existing API.
When I follow the directions on https://next-auth.js.org/configuration/callbacks
like this:
callbacks: {
async jwt({ token, user }) {
if (user) {
token.accessToken = user.jwt
}
return token
},
async session({ session, token, user }) {
session.accessToken = token.accessToken
return session
}
}
the resulting session object from useSession() looks like this:
{
expires: "2022-03-22T18:29:02.799Z",
user: {email: 'john#nextIsGreat.com'}
}
I can't use that as it does not have the token available.
So I was able to make up my own working solution, but it is kind of strange because of the way things are grouped together. Here is what I am doing now, that I am trying to figure out how to do better. I use comments to point out the problem areas:
[...nextauth].js:
import NextAuth from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import axios from 'axios'
export default NextAuth({
providers: [
Credentials({
name: 'Email and Password',
credentials: {
username: { label: 'Username', type: 'text', placeholder: 'jsmith' },
password: { label: 'Password', type: 'password' }
},
authorize: async (credentials) => {
const url = process.env.API_URL + '/authenticate'
const result = await axios.post(url, {
username: credentials.username,
password: credentials.password
})
const user = result.data
console.log(user)
//It logs this:
/*
{
jwt: 'eyJhbasU1OTJ9.NQ356H4Odya62KmN...', //<---***This is the token i pass in to all of my API calls****
user: {
userId: 207,
email: 'john#nextIsGreat.com',
firstName: 'John',
lastName: 'Doe',
roleId: 1,
}
}
*/
if (user) {
return Promise.resolve(user)
} else {
return Promise.resolve(null)
}
}
})
],
callbacks: {
async jwt({ token, user }) {
if (user) {
if (user.jwt) {
token = { accessToken: user.jwt, restOfUser: user.user }
}
}
return token
},
async session(seshProps) {
return seshProps
}
}
})
Home.js:
export const Home = () => {
const { data: session } = useSession()
console.log(session)
//LOGS THIS --->
/*
{
"session": { "user":{}, "expires":"2022-03-22T17:06:26.937Z"},
"token":{
"accessToken":"eyJ...",
"iat":1645376785,
"exp":1647968785,
"jti":"41636a35-7b9a-42fd-8ded-d3dfgh123455a"
"restOfUser": {
"userId":207,
"email":"john#nextIsGreat.com",
"firstName":"John",
"lastName":"Doe",
"roleId":1
}
}
{
*/
const getPosts=()=> {
const url = 'localhost:4000/posts'
const {data} = axios.get(url, {
Authorization: session.token.accessToken <--**This is the way I am calling my API
})
console.log(data)
}
return (
<div onClick={getPosts}>
Hello, {session.token.restOfUser.firstName}
/* I have to access it like this now, which seems wrong ***** */
</div>
)
}
Cheers for creating your own solution but you do not need it. NextAuth CredentialsProvider handles it already by setting your NextAuth session configuration to session: {strategy: "jwt", ... }.
You can also remove your callbacks for jwt() and session() and remove your owned generated JWT access token. As you do not need it, this way you can authenticate your existing system.
And at your CredentialsProvider({authorize(){}} authorize method. If you had directly connected to the user database, you can directly look up the user credential without doing a post request since it is already considered a server-side function.
I implemented this in next-auth following some tutorial online
import NextAuth from "next-auth"
import Providers from "next-auth/providers";
const https = require('https');
export default NextAuth({
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
const url = 'https://localhost/auth';
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
});
const res = await fetch(url, {
method: 'POST',
body: JSON.stringify(credentials),
agent: httpsAgent,
headers: {
"Content-Type": "application/json"
}
})
const user = await res.json();
if (res.ok && user) {
return user;
} else {
return null;
}
}
}),
// ...add more providers here
],
callbacks: {
async jwt(token, user, account, profile, isNewUser) {
if (user?.type) {
token.status = user.type
}
if (user?.username) {
token.username = user.username;
}
return token
},
async session(session, token) {
session.type = token.type;
session.username = token.username;
return session
}
}
})
pretty standard. https://localhost/auth return an object like this (I called it user for now)
{
token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2MzY0MTE4NjEsImV4cCI6MTYzNjQxNTQ2MSwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiZXJuYTM5QHdlYmVyLmNvbSJ9.Abenx1GhB-_d9LVpLfa2NYp62Lbw6U65EUQowA0jA_aykx1m-BlBR_YBcL4XIJsknJ99NN8Ees4Zxdsphfhjs7du4TR2MgTITHYy-BYjBX9CsluVSBpm-L7c-oK5vu70eumAy1ixy5MKOTN2EQYCm65RszSheIwZ4LN8vSuzxzZuLszRG9nbpauiHDpYCeLrNeNkz4lhTicfWkdPafR8vhqt4MIeCl-kxbMqc35UNmglzE7n-b9zVh4OhU7bSCoPKZySL5c4GSf7UFFD-mXIe6s9b4qYSXJuLpdspFJSgP7UoEGP1gh8fTb5MDZREYyZOpK3BMU8EdwokngVR9zrbw'
}
I would like to know how to store this token to be used in further calls to my API. I can see the token object in the session callback is
{ iat: 1636411862, exp: 1639003862 }
so next-aut is not doing this for me. Should I set an httpOnly cookie in the session callback? or right after
if (res.ok && user) {
just before to return user?
I found a way just updating the callbacks:
callbacks: {
async jwt(token, user, account, profile, isNewUser) {
if (user?.token) {
token.token = user.token;
}
return token;
},
async session(session, token) {
return session;
}
}
in this way the token from the API is now stored in a httpOnly cookie called __Secure-next-auth.session-token (assuming the token from the API is in the format like above).
If you store the JWT in the cookies so every time you're calling your API you could check the cookie header to see if you have it.