I have a Meteor application with the following fixtures code:
/imports/startup/server/fixtures.js
import { Meteor } from 'meteor/meteor'
import { Accounts } from 'meteor/accounts-base'
if(Meteor.users.find().count === 0) {
const users = [
{username: "admin", email: "admin#testing.demo", profile: { name: "Admin" }, roles: ["admin"]},
{username: "school", email: "school#testing.demo", profile: { name: "School Name" }, roles: ["school"]},
{username: "teacher", email: "teacher#testing.demo", profile: { name: "Teacher" }, roles:["teacher"]}
]
for(let user of users) {
Accounts.createUser({
username: user.username,
email: user.email,
password: "123456",
profile: {
name: user.profile.name,
},
roles: user.roles
})
}
}
On starting up my project all the accounts are created successfully except none of them have the roles field. What am I doing wrong?
What you are doing wrong is that you pass options into the function that are not accepted by it. createUser options only accepts username, email, password and profile. You should study the docs, meteors API is very well documented.
Now, to set the user roles you have a couple of options, one of them would be use the _id of the newly created user, which is returned by createUser and then set the roles like so:
const userId = Accounts.createUser({
username: user.username,
email: user.email,
password: "123456",
profile: {
name: user.profile.name,
});
Roles.addUsersToRoles(userId, user.roles)
assuming that this is server side code. On the client this won't work. You could also set the roles directly using a Meteor.users.update(); call or fiddle around with Accounts.onCreateUser callback, which is very nice for manipulating everything that you pass into createUser. Hope that helps.
Related
How can i create a simple reset password using next auth Credential provider with mongo db
i have user schema like this
const userSchema = new mongoose.Schema<UserSchemaType>(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
img: { type: String, required: true },
isAdmin: { type: Boolean, required: true, default: false },
},
{
timestamps: true,
}
);
my next auth look like this
providers: [
CredentialsProvider({
async authorize(credentials) {
await db.connect();
const user = await User.findOne({
email: credentials.email,
});
if (user && bcryptjs.compareSync(credentials.password, user.password)) {
return {
_id: user._id,
name: user.name,
email: user.email,
image: user.img,
isAdmin: user.isAdmin,
};
}
throw new Error('Invalid email or password');
},
}),
],
});
is there a simple example for implementing reset password with the next auth
From the docs - https://next-auth.js.org/providers/credentials
The functionality provided for credentials based authentication is
intentionally limited to discourage use of passwords due to the
inherent security risks associated with them and the additional
complexity associated with supporting usernames and passwords.
Probably you can use the Email Provider and customise if you really want this feature.
https://next-auth.js.org/configuration/providers/email
I try to get credentials auth with next-auth, but I have no experience to use it and whatever I do, i get following message:
[next-auth][error][callback_credentials_jwt_error] Signin in with credentials is only supported if JSON Web Tokens are enabled
https://next-auth.js.org/errors#callback_credentials_jwt_error
This is my src/pages/api/auth/[...nextauth].js file.
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import User from "#models/User"
const options = {
NEXTAUTH_URL:process.env.NEXTAUTH_URL,
providers: [
Providers.Credentials({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: 'Avista',
// The credentials is used to generate a suitable form on the sign in page.
// You can specify whatever fields you are expecting to be submitted.
// e.g. domain, username, password, 2FA token, etc.
credentials: {
email: {label: "Email", type: "text"},
password: {label: "Password", type: "password"}
},
authorize: async (credentials) => {
// Add logic here to look up the user from the credentials supplied
// const user = {id: 1, name: 'J Smith', email: 'jsmith#example.com'}
const user = await User.findOne()
console.log("Данные входа", credentials)
if (user) {
// Any object returned will be saved in `user` property of the JWT
return Promise.resolve(user)
} else {
// If you return null or false then the credentials will be rejected
return Promise.resolve(null)
// You can also Reject this callback with an Error or with a URL:
// return Promise.reject(new Error('error message')) // Redirect to error page
// return Promise.reject('/path/to/redirect') // Redirect to a URL
}
}
}),
Providers.Email({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD
}
},
from: process.env.EMAIL_FROM
}),
],
// A database is optional, but required to persist accounts in a database
database: process.env.MONGO_URI,
secret: process.env.JWT_SIGNING_PRIVATE_KEY,
},
}
I don't know what I do wrong.
try adding this to your [...nextauth].js page
session: {
jwt: true,
// Seconds - How long until an idle session expires and is no longer valid.
maxAge: 30 * 24 * 60 * 60, // 30 days
},
read more about the options here: https://next-auth.js.org/configuration/options#jwt
This might be useful for someone else.
You need to specify that you want to use the jwt auth style.
Make options.session to be:
{ jwt: true }
If you using next-auth version 4, you should instead do:
{ strategy: 'jwt'}
Add this after providers: []
callbacks: {
jwt: async ({ token, user }) => {
user && (token.user = user)
return token;
},
session: async ({ session, token }) => {
session.user = token.user
return session;
}
},
secret: "secret",
jwt: {
secret: "ksdkfsldferSDFSDFSDf",
encryption: true,
},
I have a cloud function that is triggered by Auth user creation. I look up the user data (email, name, etc) to populate my DB. It suddenly stopped working for the 'email/password' Auth provider type. The admin.auth().getUser(uid) now returns a userRecord which contains undefined/null values for most fields. This seemingly stopped working out of nowhere in Production after functioning for several weeks, is there any possible explanation?
exports.createUser = functions.auth.user().onCreate((user) => {
return createEmailUser(user);
});
function createEmailUser(user) {
const uid = user.uid;
return admin.auth().getUser(uid)
.then(function(userRecord) {
console.log(userRecord);
const email = userRecord.email;
const fullName = userRecord.displayName;
admin.database().ref('users/' + uid).set({
email: email,
name: fullName
});
})
.catch(function(error) {
console.log("Error fetching user data:", error);
});
}
In the past, the userRecord object contains valid email and displayName values. Now, I see an object like this:
UserRecord {
uid: 'Lrtj8zafsnYjZl4ckMgwNkgEiVH2',
email: undefined,
emailVerified: false,
displayName: undefined,
photoURL: undefined,
phoneNumber: undefined,
disabled: false,
metadata:
UserMetadata {
creationTime: 'Wed, 09 Jan 2019 21:40:31 GMT',
lastSignInTime: null },
providerData: [],
passwordHash: undefined,
passwordSalt: undefined,
customClaims: undefined,
tokensValidAfterTime: 'Wed, 09 Jan 2019 21:40:31 GMT' }
As users are registered with Email/Password method, then there is only the email address available in userRecord. Other sign-in providers might have different data at user creation.
What you can do here is to check user data at profile creation, and update profile with updateUser if anything is missing:
function createEmailUser(user) {
const uid = user.uid;
admin.auth().updateUser(uid, {
phoneNumber: "+11234567890",
displayName: "Foo Bar"
})
.then(function(userRecord) {
console.log(userRecord);
})
.catch(function(error) {
console.log("Error fetching user data:", error);
});
}
I'm almost good with all my tests for Firestore Rules. But, I still need to test some path for the admin.
The admin in my app is not the Firebase admin, it's an user with privileges set like this in its customClaims :
claims: {admin: true}
How I can mock users customClaims with the npm package #firebase/testing ? I can't figure it out.
Thanks
PS: Below, what I'm currently doing.
export default class TestApplication {
public static getFirestoreInstance(auth, projectId: string): firebase.firestore.Firestore {
return firebase
.initializeTestApp({projectId, auth})
.firestore();
}
}
describe(){
const defaultAuthUser = {uid: "alice", email: "alice#example.com", "token": {"admin": true};
before(done => {
currentProjectId = TestUtils.getRandomId();
TestApplication.useFirestoreRules(currentProjectId, rules)
.then(() => done())
.catch(done);
});
it("I should be able to get another user if I'm an admin", () => {
return firebase.assertSucceeds(
TestApplication.getFirestoreInstance(defaultAuthUser, currentProjectId)
.collection(FirebaseReference.USERS_REF)
.doc(otherUserId)
.get()
);
}).timeout(5000);
}
Custom claims can be passed as top-level fields within the auth object:
firebase.initializeTestApp({
projectId: "my-test-project",
auth: { uid: "alice", my_custom_claim: "foo" }
});
In the example from the question, this line:
const defaultAuthUser = {uid: "alice", email: "alice#example.com", "token": {"admin": true};
Should be changed to:
const defaultAuthUser = {uid: "alice", email: "alice#example.com", admin: true};
I think the answer above could be a little more specific.
The custom claim needs to be on the auth object when initializing the app. But when checking the custom claims in the security rules the custom claims are in the 'token' property (request.auth.token.admin).
Initializing the app:
firebase.initializeTestApp({
projectId: "my-test-project",
auth: { uid: "alice", my_custom_claim: "foo", admin: true }
});
firebase.rules
...
match /posts/{postId} {
allow update: if request.auth.token.admin
}
...
I suppose you are using this approach:
https://firebase.google.com/docs/firestore/security/test-rules-emulator#run_local_tests
So somewhere in your test code you have:
firebase.initializeTestApp({
projectId: "my-test-project",
auth: { uid: "alice", email: "alice#example.com" }
});
In this auth field you can mock your custom claims as:
{
"uid": "alice",
"email": "alice#example.com",
"token": {
"admin" :true
}
}
Then you can use request.auth.token.admin in your rules. I tried this on storage, but same/similar should apply to Firestore too.
Is there a way to seed users into accounts-password? Something like:
Users.insert
email: 'test#test.com'
password: 'secret'
I've tried many things but no luck yet. Thanks in advance for the help
It's easiest to do this on the server when it starts:
Meteor.startup(function() {
if (Meteor.users.find().count() === 0) {
Accounts.createUser({
username: 'test',
email: 'test#example.com',
password: 'password'
});
}
});