I am trying to make an email verification in my project using this tutorial:
https://themeteorchef.com/tutorials/sign-up-with-email-verification
I've got method:
//server/methods/send-email.js
import { Meteor } from 'meteor/meteor';
import { Email } from 'meteor/email';
Meteor.methods({
sendVerificationLink() {
let userId = Meteor.userId();
if ( userId ) {
console.log("Email has been sent");
return Accounts.sendVerificationEmail( userId );
}
}
});
And call this method on client side:
//client/form.js
handleSubmit = () => {
this.validateField('phone', this.state.phone)
if (this.state.formValid) {
this.update_User({phone: this.state.phone});
}
Meteor.call( 'sendVerificationLink', ( error, response ) => {
if ( error ) {
console.log(error);
} else {
console.log("- There is no errors in Meteor.call -");
}
});
}
I am getting an email with link. But when I go this link, nothing happens.
Meteor.user().emails[ 0 ].verified - doesn't become true.
{Meteor.user().emails[ 0 ].verified ? <div>Success</div> : <div>Failed</div>}
I don't get the Success text.
I've tried this:
import { Meteor } from 'meteor/meteor';
Accounts.emailTemplates.siteName = "name";
Accounts.emailTemplates.from = "name<admin#name.io>";
Accounts.emailTemplates.verifyEmail = {
subject() {
return "[name] Verify Your Email Address";
},
text( user, url ) {
let emailAddress = user.emails[0].address,
urlWithoutHash = url.replace( '#/', '' ),
supportEmail = "support#cryptocean.io",
emailBody = `To verify your email address (${emailAddress}) visit the following link:\n\n${urlWithoutHash}\n\n If you did not request this verification, please ignore this email. If you feel something is wrong, please contact our support team: ${supportEmail}.`;
return emailBody;
}
};
Accounts.onEmailVerificationLink = function() {
console.log("Verified");
user.emails[0].verified = true;
}
But it seems I'm doing something wrong.
I am not very experienced in Meteor / backend... So I really hope to find some help here. Imagine cat looking deeply into your soul from "Puss in Boots" movie. That's me right now))
Like Deepak suggested, in react router that would be something like:
<Route exact path='/reset-password/:token' component={ResetPasswordPage} />
<Route exact path='/verify-email/:token' component={VerifyEmailPage} />
and the VerifyEmailPage could look like:
import React, { Component } from 'react'
import { Accounts } from 'meteor/accounts-base'
export default class VerifyEmailPage extends Component {
componentDidMount () {
const token = this.props.match.params.token
Accounts.verifyEmail(token, (err) => {
if (err) {
toastr.error('Could not verify email!', err.reason)
} else {
toastr.success('Email confirmed successfully!')
this.props.history.push('/feeds')
}
})
}
render () {
return (
<div>{''}</div>
)
}
}
You need to setup a route where you can get and verify the verification token.
Something like what they have done here, in the tutorial that you are following.
Basically, get the verification token on the frontend, call the Account method Accounts.verifyEmail with verification token as parameter.
How about you remove the "return" from
return Accounts.sendVerificationEmail( userId )
and try again.
Related
I am using azure communication services in my react app to send email.
But It is giving CORS error
import { EmailClient } from "#azure/communication-email";
function App() {
const connectionString =
"**************************************************************************************";
const client = new EmailClient(connectionString);
const sender = "1000055393#hexaware.com";
const emailContent = {
subject: "Send email quick start test- JS sample",
plainText:
"Test Email from JS Send Email Sample Application\n\n This email is part of testing of email communication service. \\n Best wishes",
html: "<html><head><title>ACS Email as a Service</title></head><body><h1>ACS Email as a Service - Html body</h1><h2>This email is part of testing of email communication service</h2></body></html>",
};
const toRecipients = {
to: [{ email: "krnsda04#gmail.com", displayName: "Karan S" }],
};
async function main() {
try {
const emailMessage = {
sender: sender,
content: emailContent,
recipients: toRecipients,
};
console.log(sender,"sender");
const sendResult = await client.send(emailMessage);
console.log(sendResult,"result");
if (sendResult && sendResult.messageId) {
// check mail status, wait for 5 seconds, check for 60 seconds.
const messageId = sendResult.messageId;
if (messageId === null) {
console.log("Message Id not found.");
return;
}
console.log("Send email success, MessageId :", messageId);
let counter = 0;
const statusInterval = setInterval(async function () {
counter++;
try {
const response = await client.getSendStatus(messageId);
if (response) {
console.log(
`Email status for {${messageId}} : [${response.status}]`
);
if (response.status.toLowerCase() !== "queued" || counter > 12) {
clearInterval(statusInterval);
}
}
return;
} catch (e) {
console.log("Error in checking send mail status: ", e);
}
}, 5000);
} else {
console.error(
"Something went wrong when trying to send this email: ",
sendResult
);
}
return;
} catch (e) {
console.log(
"################### Exception occoured while sending email #####################",
e
);
}
}
main();
return <h3>hello ${connectionString}</h3>;
}
export default App;
But when running this code, I m getting this error.
Access to XMLHttpRequest at 'https://************************/emails:send?' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
In Azure docs, they have given the code in Node.js.
Since Iam using that code in react and accessing from front end, is it showing CORS error?
How to solve this issue?
As you can see in the SDKs and REST APIs docs, ACS Email library is meant to be used in a trusted service environment. This is because there is no reasonable way to protect your connection string in the browser.
In your case, building a Node.js backend and letting your React app talk to it would be a good and preferred solution.
I've one question. I don't understand why, but my services is undefined. Someone could help me to clarify this ?
I've a component "FormLogin" with the call of this service
<script>
import { authenticationService } from '#/container.js'
import { ref } from '#vue/reactivity'
export default {
emits: ['successfullyLogged'],
setup (props, context) {
const errors = ref([])
const email = ref(null)
const password = ref(null)
const submit = () => {
errors.value = []
authenticationService()
.login(email.value, password.value)
.then(() => {
context.emit('successfullyLogged')
})
.catch(error => {
errors.value = [error.response.data.message]
})
}
return {
email,
password,
errors,
submit
}
}
}
</script>
Then i've the "global injection"
import api from '#/clients/api.js'
import TokenRepository from '#/repositories/TokenRepository.js'
import AuthenticationService from '#/services/AuthenticationService.js'
export function tokenRepository () {
return new TokenRepository()
}
export function authenticationService () {
return new AuthenticationService(api, tokenRepository)
}
And after that, the service itself
import store from "#/store"
export default (client, tokenRepository) => {
const login = (email, password) => {
return client.post('/oauth/token', {
grant_type: 'password',
client_id: process.env.VUE_APP_CLIENT_ID,
client_secret: process.env.VUE_APP_CLIENT_SECRET,
username: email,
password: password
})
.then(response => {
tokenRepository().store(response.data.access_token)
store.dispatch('account/loadUser')
})
}
const logout = () => {
tokenRepository().destroy()
store.commit('account/setUser', {})
}
return {
login,
logout
}
}
But when i run this code, fill my form fields and hit the button "submit", i've this error in console, and i don't undestand why. (And when i try to use the debugger, it appear that authenticationService in FormLogin is undefined.
Thanks in advance for your help,
Christophe
So the answer of the problem was to remove the word "new" in my container.js
Bad answer for me
export function authenticationService () {
return new AuthenticationService(api, tokenRepository)
}
Good answer for me
export function authenticationService () {
return AuthenticationService(api, tokenRepository)
}
So I'm creating authentication logic in my Next.js app. I created /api/auth/login page where I handle request and if user's data is good, I'm creating a httpOnly cookie with JWT token and returning some data to frontend. That part works fine but I need some way to protect some pages so only the logged users can access them and I have problem with creating a HOC for that.
The best way I saw is to use getInitialProps but on Next.js site it says that I shouldn't use it anymore, so I thought about using getServerSideProps but that doesn't work either or I'm probably doing something wrong.
This is my HOC code:
(cookie are stored under userToken name)
import React from 'react';
const jwt = require('jsonwebtoken');
const RequireAuthentication = (WrappedComponent) => {
return WrappedComponent;
};
export async function getServerSideProps({req,res}) {
const token = req.cookies.userToken || null;
// no token so i take user to login page
if (!token) {
res.statusCode = 302;
res.setHeader('Location', '/admin/login')
return {props: {}}
} else {
// we have token so i return nothing without changing location
return;
}
}
export default RequireAuthentication;
If you have any other ideas how to handle auth in Next.js with cookies I would be grateful for help because I'm new to the server side rendering react/auth.
You should separate and extract your authentication logic from getServerSideProps into a re-usable higher-order function.
For instance, you could have the following function that would accept another function (your getServerSideProps), and would redirect to your login page if the userToken isn't set.
export function requireAuthentication(gssp) {
return async (context) => {
const { req, res } = context;
const token = req.cookies.userToken;
if (!token) {
// Redirect to login page
return {
redirect: {
destination: '/admin/login',
statusCode: 302
}
};
}
return await gssp(context); // Continue on to call `getServerSideProps` logic
}
}
You would then use it in your page by wrapping the getServerSideProps function.
// pages/index.js (or some other page)
export const getServerSideProps = requireAuthentication(context => {
// Your normal `getServerSideProps` code here
})
Based on Julio's answer, I made it work for iron-session:
import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '#/utils/index'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))
And then I use it like:
export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
return {
props: {},
}
})
My withSessionSsr function looks like:
import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'
const IRON_OPTIONS: IronSessionOptions = {
cookieName: process.env.IRON_COOKIE_NAME,
password: process.env.IRON_PASSWORD,
ttl: 60 * 2,
}
function withSessionRoute(handler: NextApiHandler) {
return withIronSessionApiRoute(handler, IRON_OPTIONS)
}
// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
return withIronSessionSsr(handler, IRON_OPTIONS)
}
export { withSessionRoute, withSessionSsr }
I am having a problem with waterlock-local-auth. Basically I've been playing around with waterlock all day trying to figure out how to create a new user (with hashed password and all), and also how to authenticate the user from a form on a server side sails.js view. But have been completely unsuccessful. Below is the code in my LoginController that my login form is posting to. Any help will be greatly appreciated. Thanks!
module.exports = {
login: function(req, res) {
var isAuthenticated = function(){...} <-- Authenticated by waterlocks
if(isAuthenticated) {
res.view('home');
}
else {
res.view('login', {errorMessage: "Invalid username or password"});
}
}
};
Ok, so basically I went with the solution posted here (Sails.js Waterlock /auth/register causes error 500). ;0)
module.exports = require('waterlock').waterlocked({
// Endpoint for registering new users. Taken from: https://stackoverflow.com/questions/29944905/sails-js-waterlock-auth-register-causes-error-500/29949255#29949255
register: function (req, res) {
var params = req.params.all(),
def = waterlock.Auth.definition,
criteria = {},
scopeKey = def.email !== undefined ? 'email' : 'username'; // Determines if the credentials are using username or emailaddess.
var attr = { password: params.password }
attr[scopeKey] = params[scopeKey];
criteria[scopeKey] = attr[scopeKey];
waterlock.engine.findAuth(criteria, function (err, user) {
if (user)
return res.badRequest("User already exists");
else
waterlock.engine.findOrCreateAuth(criteria, attr, function (err, user) {
if (err)
return res.badRequest(err);
delete user.password;
return res.ok(user);
});
});
}
});
I have created a login which is able to login a user and store the user if they are new in the database.
The user is then redirected to / and then is checked if they are authenticated or not, see below (app.js):
.get('/', function* () {
if (this.isAuthenticated()) {
yield this.render('homeSecure', {}); // <-- need user data here
} else {
yield this.render('homePublic', {});
}
As I commented in the code, I would like to send the user object of which is logged in. I have no idea how to get a hold of the id of the person logged in as the documentation for koa in general is not as complete as that of express.
I am using koa-generic-session-mongo to handle my sessions. Here is my GoogleStrategy (auth.js):
var user = null;
// ...
var GoogleStrategy = require('passport-google').Strategy;
passport.use(new GoogleStrategy({
returnURL: 'http://localhost:' + (process.env.PORT || 3000) + '/auth/google/callback',
realm: 'http://localhost:' + (process.env.PORT || 3000)
},
function (identifier, profile, done) {
var emails = new Array();
for (var i = 0; i < profile.emails.length; i++) {
emails.push(profile.emails[i].value);
}
co(function* () {
yield users.findOne({
emails: emails
});
});
if (user === null) { // first time signin, create account
co(function* () {
user = {
id: 1,
name: profile.displayName,
emails: emails
};
yield users.insert(user);
});
}
console.log(user);
done(null, user);
}));
publicRouter
.get('/', function* () {
if (this.isAuthenticated()) {
yield this.render('homeSecure', {
user: this.req.user
});
} else {
yield this.render('homePublic', {});
}
})...
Disclaimer: I've not used koa-passport, I've just looked at the code.
According to the source code of the koa-passport library, the property you're looking for is passport.user, and is used like so:
app.use( function*(){
var user = this.passport.user
})
Thus, your code sample would become
.get('/', function* () {
if (this.isAuthenticated()) {
yield this.render('homeSecure', this.passport.user );
} else {
yield this.render('homePublic', {});
}
If that does not work, this file leads me to suspect that koa-passport follows the standard passport interface and provides this.user to the request.