Add multiple user roles #805
-
Your question What are you trying to do
As you can see I have 2 routes, and it actually works because I can see my custom form in both routes but when a sign in, it only works for the Feedback
|
Beta Was this translation helpful? Give feedback.
Replies: 9 comments 29 replies
-
I had assumed this was par for the course, but it seems that no one has role-based authentication working in next-auth... |
Beta Was this translation helpful? Give feedback.
-
I have roles working. models/index.js import User, { UserSchema } from "./User"
export default {
User: {
model: User,
schema: UserSchema
}
} models/User.js import Adapters from "next-auth/adapters"
// Extend the built-in models using class inheritance
export default class User extends Adapters.TypeORM.Models.User.model {
constructor(name, email, image, emailVerified, roles) {
super(name, email, image, emailVerified)
if (roles) { this.roles = roles}
}
}
export const UserSchema = {
name: "User",
target: User,
columns: {
...Adapters.TypeORM.Models.User.schema.columns,
roles: {
type: "varchar",
nullable: true
},
},
} pages/api/auth/[...nextauth].js import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import Adapters from 'next-auth/adapters'
import Models from '../../../models'
export default NextAuth({
// @link https://next-auth.js.org/configuration/providers
providers: [
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET
})
],
// @link https://next-auth.js.org/tutorials/typeorm-custom-models
adapter: Adapters.TypeORM.Adapter(
process.env.DATABASE_URL,
{
models: {
User: Models.User
}
}
),
session: { jwt: true },
callbacks: {
async jwt(token, user, account, profile, isNewUser) {
if (account?.accessToken) {
token.accessToken = account.accessToken
}
if (user?.roles) {
token.roles = user.roles
}
return token
},
async session(session, token) {
if(token?.accessToken) {
session.accessToken = token.accessToken
}
if (token?.roles) {
session.user.roles = token.roles
}
return session
}
}
}); Usage - in JSX {session.user.roles && session.user.roles.includes("Admin") && <>
<button id="doSomething">Admin Only</button>
</>} |
Beta Was this translation helpful? Give feedback.
-
That does seem a bit odd. Does the DATABASE_URL have a protocol at the
beginning of the connection string, like mongodb://?
TypeORM would need the protocol prefix to identify which driver to use.
You could hand a connection string without a protocol to most DB drivers.
That would align with the pattern you are describing.
…On Tue, Mar 30, 2021, 8:24 PM michaelcmelton ***@***.***> wrote:
Looking at the code you submitted, everything seems in order. My first
guess based on the symptoms would be an issue with the value of
*process.env.DATABASE_URL*.
That’s the odd part. When I pass in that value through the database
property, and comment out the adapters property, I can connect to the db.
It doesn’t work here for some reason.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#805 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AGGYIEGUYCACRYC4M5UMRSLTGKIYNANCNFSM4V4VTRAA>
.
|
Beta Was this translation helpful? Give feedback.
-
To reduce the number of database queries needed to get role-based authentication working, I have used a combo of JWTs and a so-called With this, you have the benefit of not needing to retrieve a session and subsequently a user from a database on every session-requiring request (saves two queries), whilst also having no worries about authorising against stale roles in JWTs. The Personally, I use the roles in the JWT for things like aesthetic client-side rendering, and rely on the I am working with Fauna and a copy of the Fauna adapter. The example implementation below can be adapted to your own needs. Example ImplementationGateThe gate accepts a user ID and Fauna DB client, and returns some neat methods to determine if a person can do a thing. import { query as q } from 'faunadb';
/**
* Gate - returns a promised object containing methods to determine
* if a given user is authorised against a give role(s)
*
* The gate fetches a copy of the user from a server-side store, rather than
* relying on a session object directly. Since it is not easy to invalidate JWTs,
* the local session's associated roles could become stale. Checking with the
* server-side store every time, whilst introducing a read operations,
* more robustly enforces permissions as it relies a single source of truth.
*
* @param {string} userId - the ID of the user to gate against.
* @param {object} faunaClient - an instance of a Fauna DB client.
* @returns {Promise}
*/
const Gate = async (userId, faunaClient) => {
try {
/**
* Retrieve the user and their given roles from the server-side store.
* If no user is returned, set an empty roles array to ensure all
* gate methods return false, denying authorisation for any gated action.
*/
const FQL = q.Get(q.Ref(q.Collection('users'), userId));
const { data: user } = (await faunaClient.query(FQL)) || { data: { roles: [] } };
/**
* Determines if a user is assigned a given role.
* @param {string} role - the roles to check against.
* @returns {boolean}
*/
const allows = (role) => user.roles.includes(role);
/**
* Determines if a user is assigned all given roles.
* @param {[string]} roles - the roles to check against.
* @returns {boolean}
*/
const all = (roles) =>
roles
.map((role) => user.roles.includes(role))
.reduce((allIncludes, includes) => (includes ? allIncludes : false), true);
/**
* Determines if a user is assigned at least one of the given roles.
* @param {[string]} roles - the roles to check against.
* @returns {boolean}
*/
const any = (roles) =>
roles
.map((role) => user.roles.includes(role))
.reduce((allIncludes, includes) => (includes ? true : allIncludes), false);
/**
* Determines which given roles are missing from the user's roles.
* @param {[string]} roles - the roles to check against.
* @returns {[string]}
*/
const missing = (roles) => roles.filter((role) => !user.roles.includes(role));
return { allows, all, any, missing };
} catch {
return Promise.reject(new Error('gate_error_fetching_user'));
}
};
export default Gate; Fauna Adapter ChangesAll the roles field to any new users, which unfortunately involves modifying the Fauna adapter. ---
function createUser(profile) {
logger.debug('create_user', profile);
const FQL = q.Create(q.Collection(collections.User), {
data: {
name: profile.name,
email: profile.email,
image: profile.image,
emailVerified: profile.emailVerified
? q.Time(profile.emailVerified.toISOString())
: null,
username: profile.username,
createdAt: q.Now(),
updatedAt: q.Now(),
roles: ['user'] // Add roles definiton
}
});
---
|
Beta Was this translation helpful? Give feedback.
-
nope. I shelf the project until there's more documentation on it
…On Mon, Feb 21, 2022 at 2:47 AM Pelps12 ***@***.***> wrote:
@myhendry <https://github.com/myhendry> Were you able to get it to work
with the new mongoDB adapter?
—
Reply to this email directly, view it on GitHub
<#805 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AE5PYGZ52AFZG6JGB2EXPM3U4EZKJANCNFSM4V4VTRAA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
Using a database provider strategy: First modify the 'user' table to include a "roll" row. I'm using the Prisma ORM so i changed the 'user' table to include row as shown below:
I modified the session callback in the [...nextauth].js file as shown below
Now with both frontend and backend API get session hooks i can:
How you roll your own update API, for setting users as admin, is up to you. You might create a users.super column that can only be updated with direct database access. |
Beta Was this translation helpful? Give feedback.
-
Hi everyone, I'm currently facing an issue with my admin panel. Although the database and roles have been updated successfully, I'm having trouble with the token not being updated accordingly. As a result, I would like to know how to invalidate another user's session, so that they are prompted to sign in again after their role has been changed. Thank you in advance for your assistance. Best regards 🚀 |
Beta Was this translation helpful? Give feedback.
-
i make multiple role base access and i used the t3stack setup |
Beta Was this translation helpful? Give feedback.
-
I am using a Google provider + JWT strategy and want to assign different roles. What do u suggest? |
Beta Was this translation helpful? Give feedback.
I have roles working.
models/index.js
models/User.js
pages/api/auth/[.…