Can I use Supertokens with linkedin login oauth 2.0 as Third party provider ? How to do so? - authentication

I was researching about identity providers and I found supertokens (https://supertokens.com/docs/guides), it seems a nice solution but I would like to know if it also accepts LinkedIn as a third party provider because I couldn't see any info about this in docs or in any other related post. Also if you have any example code would be awesome
Find documentation about integration between supertokens and LinkedIn auth, couldn't find any.

The documentation for implementing custom providers is available here - https://supertokens.com/docs/thirdparty/common-customizations/sign-in-and-up/custom-providers. Based on that, here is a sample implementation for linkedin that you could use:
Frontend:
export const SuperTokensConfig = {
appInfo: {
appName: "SuperTokens Demo App",
apiDomain: "http://localhost:3001",
websiteDomain: "http://localhost:3000",
},
// recipeList contains all the modules that you want to
// use from SuperTokens. See the full list here: https://supertokens.com/docs/guides
recipeList: [
ThirdParty.init({
signInAndUpFeature: {
providers: [
{
id: "linkedin",
name: "Linkedin",
buttonComponent: <div style={{
cursor: "pointer",
border: "1",
paddingTop: "5px",
paddingBottom: "5px",
borderRadius: "5px",
borderStyle: "solid"
}}>Login with Linkedin</div>
}
],
},
}),
Session.init(),
],
};
Backend:
const Linkedin = (config: any): TypeProvider => {
return {
id: "linkedin",
get: (redirectURI: string | undefined, authCodeFromRequest: string | undefined, userContext: any) => {
const accessTokenParams: any = {
client_id: config.clientId,
client_secret: config.clientSecret,
grant_type: "authorization_code",
}
if (redirectURI !== undefined) accessTokenParams["redirect_uri"] = redirectURI;
if (authCodeFromRequest !== undefined) accessTokenParams["code"] = authCodeFromRequest;
const authRedirectParams: any = {
client_id: config.clientId,
scope: "r_liteprofile r_emailaddress",
response_type: "code",
}
if (redirectURI !== undefined) authRedirectParams["redirect_uri"] = redirectURI;
return {
accessTokenAPI: {
url: "https://www.linkedin.com/oauth/v2/accessToken",
params: accessTokenParams,
},
authorisationRedirect: {
url: "https://www.linkedin.com/oauth/v2/authorization",
params: authRedirectParams,
},
getClientId: () => config.clientId,
getProfileInfo: async (authCodeResponse: any, userContext: any) => {
const headers = {
Authorization: `Bearer ${authCodeResponse.access_token}`,
}
const userInfo = (await axios.get("https://api.linkedin.com/v2/me", { headers })).data
const emailInfo = (await axios.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", { headers })).data
let email = ''
for (const element of emailInfo.elements) {
if (element['handle~'].emailAddress) {
email = element['handle~'].emailAddress
break
}
}
return {
id: userInfo.id,
email: {
id: email,
isVerified: false,
}
}
},
}
},
}
}
export const SuperTokensConfig: TypeInput = {
supertokens: {
connectionURI: "https://try.supertokens.com",
},
appInfo: {
appName: "SuperTokens Demo App",
apiDomain: "http://localhost:3001",
websiteDomain: "http://localhost:3000",
},
recipeList: [
ThirdParty.init({
signInAndUpFeature: {
providers: [
Linkedin({
clientId: "...",
clientSecret: "..."
}),
],
},
}),
Session.init(),
],
};
If you are using python or the golang SDK, a similar implementation in that language should work.

Related

NextAuth giving 401 error after hosting website

I'm using next-auth credentials provider for authentication in a next.js project, it works fine in development but when I deployed the website to production I got 401 error code with the following response {url: "https://sub.domain.com/api/auth/error?error="} as I tried to login.
Everything is working fine in localhost and authentication is smooth with no errors. Wher is the error in my code?
My next-auth config
// /api/auth/[...nextauth].ts
export const authOptions: NextAuthOptions = {
secret: process.env.NEXTAUTH_SECRET ?? 'supersecret',
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
id: 'admin-login',
name: 'Admin login',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'test#test.com',
},
password: { label: 'Mot de passe', type: 'password' },
},
authorize: async (credentials, _request) => {
try {
const { data: user } = await axios.post(
`${process.env.APP_URL}/api/auth/admin/login`,
credentials
);
return user;
} catch (err) {
throw new Error(
(err as AxiosError<{ message: string }>).response?.data.message
);
}
},
}),
CredentialsProvider({
id: 'room-login',
name: 'Room login',
credentials: {
roomId: { label: 'Id de la chambre', type: 'text' },
password: { label: 'Mot de passe', type: 'password' },
},
authorize: async (credentials, _request) => {
try {
const { data: room } = await axios.post(
`${process.env.APP_URL}/api/auth/room/login`,
credentials
);
return room;
} catch (err) {
throw new Error(
(err as AxiosError<{ message: string }>).response?.data.message
);
}
},
}),
],
callbacks: {
async signIn() {
return true;
},
async redirect({ url, baseUrl }) {
if (url.startsWith('/')) return `${baseUrl}${url}`;
else if (new URL(url).origin === baseUrl) return url;
return baseUrl;
},
async jwt({ token, user }) {
if (user) {
token.id = user.id;
token.role = (user.role as Role) ?? 'GUEST';
}
return token;
},
async session({ session, token }) {
const sess: Session = {
...session,
user: {
...session.user,
id: token.id as number | string,
role: token.role as Role,
},
};
// console.log('SESSION: ', sess)
return sess;
},
},
session: {
strategy: 'jwt',
},
jwt: {
secret: process.env.JWT_SECRET ?? 'supersecret',
maxAge: 10 * 24 * 30 * 60, // 30 days
},
pages: {
signIn: '/auth/login',
signOut: '/auth/login',
newUser: '/api/auth/register',
error: '/auth/login',
},
debug: process.env.NODE_ENV === 'development',
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
return NextAuth(req, res, authOptions);
}
The issue was caused by the SSL certificate that I had to install on the domain for next-auth to work.

like/dislike button with api call not working using vue an mongoDB

I am learning vuejs and i am working on my first project which is a social network, and i want to implement a like button that call the api to add a like or remove it if the user has already liked it. It does work in my backend but i can't make it work in the front.
I need to send the userId and add or remove the like when i click on the button
This is the data
data() {
return {
post: {
file: "",
content: "",
likes: 0,
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
the last method i tried
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then(() => {
console.log("response", response);
this.user._id = response.data._id;
if(post.usersLiked == user._id) {
this.post.likes += 0
} else if (post.usersLiked != user._id) {
this.post.likes += 1
};
})
.catch((error) => console.log(error));
}
and this is the model
const postSchema = mongoose.Schema({
userId: { type: String, required: true, ref: "User" },
content: { type: String, required: true, trim: true },
imageUrl: { type: String, trim: true },
likes: { type: Number, default: 0 },
usersLiked: [{ type: String, ref: "User" }],
firstname: {type: String, required: true, trim: true },
lastname: {type: String, required: true, trim: true },
created_at: { type: Date},
updated_at: { type: Date }
});
Any idea what is wrong ? Thank you !
.then(() => { // you missed value response from Promise here
this.user._id = response.data._id;
if(post.usersLiked == user._id)
})
Do you mean this.post.usersLiked === user._id I suppose, so post within your data options should be
post: {
file: "",
content: "",
likes: 0,
usersLiked: false,
// something else reflect to your post schema
},
i want to implement a like button that call the api to add a like or remove it if the user has already liked it
By saying that you just need a simple boolean value to do this
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
// Just need to toggle state
this.$set(this.post, 'usersLiked', this.post.usersLiked !== response?.data?._id)
})
.catch((error) => console.log(error));
}
Found the answer, i changed the axios method to this
likePost(id) {
let userId = localStorage.getItem('userId');
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, { userId }, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
console.log(response.data);
this.getAllPost();
})
.catch((error) => console.log(error));
}
i also made a few changes to the data
data() {
return {
posts: [],
post: {
file: "",
content: "",
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
and i also made some changes on the controller
exports.ratePost = (req, res, next) => {
console.log(req.body.userId)
//using findOne function to find the post
Post.findOne({ _id: req.params.id }).then(post => {
if (!post.usersLiked.includes(req.body.userId)) {
// making a object with $inc and $push methods to add a like and to add the user's id
let toChange = {
$inc: { likes: +1 },
$push: { usersLiked: req.body.userId },
};
// we update the result for the like
Post.updateOne({ _id: req.params.id }, toChange)
// then we send the result and the message
.then(post =>
res
.status(200)
.json(
{ message: "Liked !", data: post }
)
)
.catch(error => res.status(400).json({ error }));
} else if (post.usersLiked.includes(req.body.userId)) {
// using the updateOne function to update the result
Post.updateOne(
{ _id: req.params.id },
// we use a pull method to take off a like
{ $pull: { usersLiked: req.body.userId }, $inc: { likes: -1 } }
)
.then(post => {
// then we send the result and the message
res
.status(200)
.json(
{ message: "Post unliked", data: post }
);
})
.catch(error => res.status(400).json({ error }));
}
});
};

How to prevent to lost session after refresh in nuxt?

I am currently working on a nuxtJS app in which the session seems to be lost after any refresh (although only in dev, not while deployed). I've tried to look up in the auth module of nuxt, and have tried many answers on google, but nothing seems to work and I'm a bit lost.
nuxt.config.js
auth: {
strategies: {
local: {
scheme: 'refresh',
token: {
property: 'token',
maxAge: 3600,
global: true,
},
refreshToken: {
property: 'refresh_token',
data: 'refresh_token',
maxAge: 60 * 60 * 24 * 30,
},
user: {
property: 'user',
},
endpoints: {
login: { url: '/authentication_token', method: 'post' },
refresh: { url: '/refresh/token', method: 'post' },
logout: false,
user: { url: '/api/user', method: 'get' },
},
autoLogout: true,
},
},
},
LoginMenu.js
methods: {
async onSubmit() {
try {
const response = await this.$auth.loginWith('local', {
data: this.login,
});
if (response.status === 200) {
await this.$auth.setUser({
email: this.login.email,
password: this.login.password,
});
await this.$router.push('/');
}
else {
this.loginFail();
}
}
catch (e) {
this.loginFail();
}
},
loginFail() {
this.showError = true;
},
},
nuxt auth.js
import Middleware from './middleware'
import { Auth, authMiddleware, ExpiredAuthSessionError } from '~auth/runtime'
// Active schemes
import { RefreshScheme } from '~auth/runtime'
Middleware.auth = authMiddleware
export default function (ctx, inject) {
// Options
const options = {
"resetOnError": false,
"ignoreExceptions": false,
"scopeKey": "scope",
"rewriteRedirects": true,
"fullPathRedirect": false,
"watchLoggedIn": true,
"redirect": {
"login": "/login",
"logout": "/",
"home": "/",
"callback": "/login"
},
"vuex": {
"namespace": "auth"
},
"cookie": {
"prefix": "auth.",
"options": {
"path": "/"
}
},
"localStorage": {
"prefix": "auth."
},
"defaultStrategy": "local"
}
// Create a new Auth instance
const $auth = new Auth(ctx, options)
// Register strategies
// local
$auth.registerStrategy('local', new RefreshScheme($auth, {
"token": {
"property": "token",
"maxAge": 3600,
"global": true
},
"refreshToken": {
"property": "refresh_token",
"data": "refresh_token",
"maxAge": 2592000
},
"user": {
"property": "user"
},
"endpoints": {
"login": {
"url": "/authentication_token",
"method": "post"
},
"refresh": {
"url": "/refresh/token",
"method": "post"
},
"logout": false,
"user": {
"url": "/api/user",
"method": "get"
}
},
"autoLogout": true,
"name": "local"
}))
// Inject it to nuxt context as $auth
inject('auth', $auth)
ctx.$auth = $auth
// Initialize auth
return $auth.init().catch(error => {
if (process.client) {
// Don't console log expired auth session errors. This error is common, and expected to happen.
// The error happens whenever the user does an ssr request (reload/initial navigation) with an expired refresh
// token. We don't want to log this as an error.
if (error instanceof ExpiredAuthSessionError) {
return
}
console.error('[ERROR] [AUTH]', error)
}
})
}

How to retrieve token property in Nuxt Auth

I am developing my first NuxtJs application with the following technologies.
For Backend APIs Laravel 7.3 (Passport)
Nuxt v2.15.2
Rendering mode: Universal (SSR / SSG)
NuxtAxios v5.13.1
NuxtAuth v5
My login API response is as follows:
{
"status": true,
"code": 200,
"data": [
{
"id": 3,
"nick_name": "johndoe",
"full_name": "John Doe",
"email": "johndoe#mail.com",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNWE5MjFmMGMyMDBlZjkxYWQxY2QyNGI3NDEyYmI1OWY4ZGEyZjg2Yzc0Y2M1ZDAwOTRhOWIyZTU4MGY3MmZmODRmNGI5N2QwYjJmYjA5NjMiLCJpYXQiOjE2MTQwMzE1MDMsIm5iZiI6MTYxNDAzMTUwMywiZXhwIjoxNjQ1NTY3NTAzLCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.clQslt3CdTLoVDHzQFwQyrRLjzjTOSeipCyQAl07gzmwXFKr542hR3MUCRr8CTjueWDTNbwd-iKkQztvB7Z-0N1zaptq67UEwj3iPEEtnbV2gMOSlVdAUu0q4OPJKYI9RwJKHnK-q1bTCOUOitfLrnYOq3Lb1T8B-3en5_S7xb9ln-iOtwVc3vW1OnEbEIsn3ELeTro1zQ9IKdlHhQ50CnGU45LipzKeadtVGkm9qm467XOqlVPZdulMJTCkvunETo23hQsTsn_Fxy0IYLUA0v-_C-ARB0N672fAuHF2a8MIYHv066Omm-6WsPrCNtfOkoIgyuMLl0gaua04IJfHrDbh7CJSEUqootKTsIVvFG4OjqR3yDN2PdhjJkPYWNTMeLbKV3ewSsjCS1aMOtXYvhrVFjrunn5M74pDBzn3kW9VMFIh2BbIfUDO_ziDrsn7KAVyOm-p7gdBN_gVKcl_Hx9x4EagixWRL7GGUqEZ2AbxRkpIO4HKwqMm7WxKatSt5hkmPZ6Zt-jfMTfrj7KuF6WhhjCh1TOFJSy9BTqp9_a2eKP-YL2M6JIJXFDHwovToC96JBptbumPvB2i2KDU_XoXF2WFx_I4iNJpZVFN0u12MeyMbXlnpf4X2t79_I7QklxSfZ5LgsOdsnt9dAm9QBbSvf8AdZZNOS4p59DuQls",
}
],
"error": null,
"errors": null
}
nuxt.config.js
auth: {
strategies: {
local: {
token: {
// property: 'access_token'
// property: 'data[0].access_token'
// property: 'data.data[0].access_token'
property: false
},
user: {
property: 'data',
autoFetch: true
},
endpoints: {
login: { url: '/en/signin', method: 'post' },
logout: { url: '/user/logout', method: 'get' },
user: { url: '/en/user/profile', method: 'get' },
}
}
}
},
Login.vue
export default {
data() {
return {
login: {
email: 'johndoe#mail.com',
password: 'welcome'
}
}
},
methods: {
async userLogin() {
try {
let response = await this.$auth.loginWith('local', { data: this.login })
console.log(response)
} catch (err) {
console.log(err)
}
}
}
}
When I am setting local.property: false, auth._token.local is being set as complete json object instead of "access_token" and then the second hit goes to '/en/user/profile' with Unauthenticated Request result.
I think an alternate way would be to set local.token.required:false , and then set the user token after your login attempt was successful. Something like this:
nuxt.config.js
auth: {
strategies: {
local: {
token: {
required: false
},
user: {
property: 'data',
autoFetch: true
},
endpoints: {
login: { url: '/en/signin', method: 'post' },
logout: { url: '/user/logout', method: 'get' },
user: { url: '/en/user/profile', method: 'get' },
}
}
}
},
Login.vue
methods: {
async userLogin() {
try {
let response = await this.$auth.loginWith('local', { data: this.login })
.then((response)=>{
this.$auth.setUserToken(response.data[0].access_token,'refreshToken');
});
console.log(response);
} catch (err) {
console.log(err)
}
}
}

Vue PayPal implementation with vue-head, paypal not defined

I'm trying to add the paypal sdk via vue-head(https://www.npmjs.com/package/vue-head) in my component but I keep getting this error:
Error in mounted hook: "ReferenceError: paypal is not defined"
What am I doing wrong here? Is the SDK simply not loading before mounted?
Is there a better way to accomplish this? Does anyone have an example of their paypal implementation in vue? Any help would be greatly appreciated.
edit: Also if I include the script tag server side (rails) then try to access paypal in vue I see this error:
Could not find driver for framework: [object Object]
<template>
<div id="paypal-button" />
</template>
<script>
import { mapState as mapConfigState } from '../scripts/store/appConfig';
export default {
props: {
totalPrice: {
type: String,
required: true,
},
currency: {
type: String,
required: true,
'default': 'USD',
},
buttonStyle: {
type: Object,
required: false,
},
},
computed: {
...mapConfigState({
customer: state => state.customer,
}),
paypalEnvironment() {
return (this.customer.paypalTestingMode) ? 'sandbox' : 'production';
},
client() {
return {
sandbox: this.customer.paypalClientIdSandbox,
production: this.customer.paypalClientIdLIVE,
};
},
},
head: {
script() {
return [
{
type: 'text/javascript',
src: `https://www.paypal.com/sdk/js?client-id=${this.client[this.paypalEnvironment]}`,
},
];
},
},
mounted() {
const total = this.totalPrice;
const currency = this.currency;
paypal.Buttons.driver(
{
env: this.paypalEnvironment,
client: this.client,
style: this.buttonStyle,
createOrder(data, actions) {
return actions.order.create({
purchase_units: [
{
amount: {
value: total,
currency,
},
},
],
});
},
onApprove(data, actions) {
return actions.order.capture();
},
}, '#paypal-button'
);
},
};
</script>
edit2: I tried adding the script in my mounted hook like this:
let el = document.querySelector(`script[src="https://www.paypal.com/sdk/js?client-id=${this.client[this.paypalEnvironment]}"]`);
if (!el) {
const src = `https://www.paypal.com/sdk/js?client-id=${this.client[this.paypalEnvironment]}`;
el = document.createElement('script');
el.type = 'text/javascript';
el.async = true;
el.src = src;
document.head.appendChild(el);
}
I can see the script in the head tag in the dev console but paypal still is not defined.
For anyone else who is trying to implement PayPal in a Vue component:
<template>
<div id="paypal-button" />
</template>
<script>
export default {
mounted() {
function loadScript(url, callback) {
const el = document.querySelector(`script[src="${url}"]`);
if (!el) {
const s = document.createElement('script');
s.setAttribute('src', url); s.onload = callback;
document.head.insertBefore(s, document.head.firstElementChild);
}
}
loadScript('https://www.paypal.com/sdk/js?client-id=sb&currency=USD', () => {
paypal.Buttons({
// Set up the transaction
createOrder(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '0.01',
},
}],
});
},
// Finalize the transaction
onApprove(data, actions) {
return actions.order.capture().then(details => {
// Show a success message to the buyer
alert(`Transaction completed by ${details.payer.name.given_name}`);
});
},
}).render('#paypal-button');
});
},
};
</script>
Alternatively you can use this: https://github.com/paypal/paypal-js