Getting server error when using custom login method - authentication

I am trying to test the custom login method functionality, so this is my client:
Meteor.loginWithCode = function(phone, code) {
Accounts.callLoginMethod({
methodArguments: [{
hascode: true,
phone: phone,
code: code
}],
userCallback: function loginCallback (error, result) {
console.log(error, result);
}
});
};
And this is the server:
Accounts.registerLoginHandler('login', function(loginRequest) {
var user = Meteor.users.findOne({phone: loginRequest.phone});
if(user.code !== loginRequest.code) {
return null;
}
var stampedToken = Accounts._generateStampedLoginToken();
var hashStampedToken = Accounts._hashStampedToken(stampedToken);
Meteor.users.update(user._id,
{$push: {'services.resume.loginTokens': hashStampedToken}}
);
return {
id: user._id,
token: stampedToken.token
};
});
Why am I getting
Exception while invoking method 'login' Error: A login method must specify a userId or an error
When I do Meteor.loginWithCode('123456789', '123');?

You should return userId not id :
Accounts.registerLoginHandler('login', function(loginRequest) {
...
...
return {
userId: user._id,
token: stampedToken.token
};
})
If login was unsuccessful then pass error instead userId.
Proof:
if (!result.userId && !result.error)
throw new Error("A login method must specify a userId or an error");
Line 255-256

Related

[uncaught application error]: TypeError - Cannot read properties of undefined (reading 'name')

I get this error message when I try to test the /api/register end point with Postman and following POST request:
{
"name" : "first",
"email" : "first#one.com",
"password" : "123"
}
[uncaught application error]: TypeError - Cannot read properties of undefined (reading 'name')
request: { url: "http://0.0.0.0:8000/api/register", method: "POST", hasBody: true }
response: { status: 404, type: undefined, hasBody: false, writable: true }
at register (file:///C:/Users/m/app_back/controllers/auth_controller.ts:9:22)
at async dispatch (https://deno.land/x/oak#v9.0.1/middleware.ts:41:7)
at async dispatch (https://deno.land/x/oak#v9.0.1/middleware.ts:41:7)
at async dispatch (https://deno.land/x/oak#v9.0.1/middleware.ts:41:7)
at async EventTarget.#handleRequest (https://deno.land/x/oak#v9.0.1/application.ts:379:9)
TypeError: Cannot read properties of undefined (reading 'name')
at register (file:///C:/Users/m/app_back/controllers/auth_controller.ts:9:22)
at async dispatch (https://deno.land/x/oak#v9.0.1/middleware.ts:41:7)
at async dispatch (https://deno.land/x/oak#v9.0.1/middleware.ts:41:7)
at async dispatch (https://deno.land/x/oak#v9.0.1/middleware.ts:41:7)
at async EventTarget.#handleRequest (https://deno.land/x/oak#v9.0.1/application.ts:379:9)
This is my auth_controller.ts file:
import {
create, verify, decode, getNumericDate, RouterContext, hashSync, compareSync
} from "../deps.ts";
import { userCollection } from "../mongo.ts";
import User from "../models/user.ts";
export class AuthController {
async register(ctx: RouterContext) {
const { value: { name, email, password } } = await ctx.request.body().value;
let user = await User.findOne({ email });
if (user) {
ctx.response.status = 422;
ctx.response.body = { message: "Email is already used" };
return;
}
const hashedPassword = hashSync(password);
user = new User({ name, email, password: hashedPassword });
await user.save();
ctx.response.status = 201;
ctx.response.body = {
id: user.id,
name: user.name,
email: user.email
};
}
async login(ctx: RouterContext) {
const { value: { email, password } } = await ctx.request.body().value;
if (!email || !password) {
ctx.response.status = 422;
ctx.response.body = { message: "Please provide email and password" };
return;
}
let user = await User.findOne({ email });
if (!user) {
ctx.response.status = 422;
ctx.response.body = { message: "Incorrect email" };
return;
}
if (!compareSync(password, user.password)) {
ctx.response.status = 422;
ctx.response.body = { message: "Incorrect password" };
return;
}
const key = await crypto.subtle.generateKey(
{ name: "HMAC", hash: "SHA-512" },
true,
["sign", "verify"],
);
const jwt = create( {
alg: "HS256",
typ: "JWT",
}, {
iss: user.email,
exp: getNumericDate(
Date.now() + parseInt(Deno.env.get("JWT_EXP_DURATION") || "0"))
},
key
);
ctx.response.body = {
id: user.id,
name: user.name,
email: user.email,
jwt,
};
}
}
export default new AuthController();
What is the problem and how can I resolve it?
EDIT: I added this line to the code:
console.log( await ctx.request.body().value );
And this is the result:
{ name: "first", email: "first#one.com", password: "123" }
You are facing this issue because you are trying to access ctx.request.body().value.value.name (notice multiple value porperties). You could change line 9 of your auth_controller.ts to this to fix it:
const { name, email, password } = await ctx.request.body().value;
On a side note, I also noticed few more issues with your current code.
Your JWT algorithm and generated secret key encryption algorithm should match
So either change your hash encryption on line 47 to SHA-256 or your JWT algorithm on line 53 to HS512.
You don't need to pass current date to getNumericDate function
This helper function already does this job for you, all you need to pass here is the time period (in seconds) when you want your token to expire. In your case it would be:
getNumericDate(Deno.env.get("JWT_EXP_DURATION") || 0)}

socket.io callback is not recevied in vue.js method?

Using this vue.js method to login users:
loginUser: function () {
socket.emit('loginUser', {
email: this.email ,
password: this.password
}, function() {
console.log('rooms in callback are:', rooms);
});
}
On the server the loginUser event is handled by:
socket.on('loginUser', (newuser,callback)=> {
var body = _.pick(newuser, ['email', 'password']);
console.log('body is:', body);
User.findByCredentials(body.email, body.password).then((user) => {
return user.generateAuthToken().then((token) => {
if (token) {
console.log('token was found');
let rooms = ['Cats', 'Dogs', 'Birds'];
callback(rooms);
} else {
socket.emit('loginFailure', {'msg' : 'Login failure'});
}
}).catch((e) => {
throw e;
});
}).catch((e) => {
socket.emit('loginFailure', {'msg' : 'Login failure'});
throw e;
});
});
I can see 'token was found' printed out in the console but does not recieve the rooms being printed in the browser console. I receive no errors either.
I'm wondering whetehr it is due to how vue.js methods work? And if so, if there is a way around it?
You forgot to specify rooms as argument in the callback
loginUser: function () {
socket.emit('loginUser', {
email: this.email ,
password: this.password
}, function(rooms) { // need to have rooms argument
console.log('rooms in callback are:', rooms);
});
}

Sequelize Model class reference error in express application

sq.js:
var Sequelize = require('sequelize');
var sequelize = new Sequelize('postgres://chandan:duvarko315#localhost:5432/diary');
var User = sequelize.define('user', {
username: {
type: Sequelize.STRING,
field: 'username' // Will result in an attribute that is firstName when user facing but first_name in the database
},
password: {
type: Sequelize.STRING
}
},{
timestamps: false,
});
User.sync({force: true}).then(function () {
// Table created
});
module.exports = User;
app.js:
var seq = require('./routes/sq');
passport.use(new passportLocal.Strategy(function(username,password, done){
User.findOne({username: username}, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null,{ id:username, name:username});
});
}));
I am getting the error: ReferenceError: User is not defined
Its because you have declared the user model as seq in app.js. Change your declaration to:
var User = require('./routes/sq');
The variable Useronly lives in sq.js

Handle challenge function not called when logging in after logout

I have created angular service, where I'm registering challengeHandler this way:
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
azureChallengeHandler.isCustomResponse = function (response) {
...
};
azureChallengeHandler.handleChallenge = function (response) {
...
};
So i'm logging in with this function:
WL.Client.login(realm, options)
And the first time it works ok, isCustomResponse gets called, returns "true", then handleChallenge gets called.
But after logging out with this function:
WL.Client.logout(realm, options)
When I try to login again, isCustomResponse gets called and still returns "true", but handleChallenge is not firing.
How can I fix that?
After calling WL.Client.reloadApp() or reloading app itself I can login again, but it's not a suitable solution.
UPDATE:
Here is adapter code:
function onAuthRequired(headers) {
return customLoginResponse(true, false, false);
}
function customLoginResponse(authRequired, azureTokenRequired, wrongTenant) {
return {
authRequired: authRequired,
azureTokenRequired: azureTokenRequired,
realm: 'AzureAuth',
wrongTenant: wrongTenant
};
}
function onLogout(){
WL.Server.setActiveUser("AzureAuth", null);
WL.Logger.debug("Logged out");
}
function submitLogout(uuid, orgId, ssogroup){
WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "removeRefreshToken",
parameters: [uuid, orgId, ssogroup]
});
onLogout();
}
function submitLogin(uuid, orgId, ssogroup, code) {
var tokenObject = getTokens(code);
if (tokenObject.id_token) {
var jwtParsed = parseJWT(tokenObject.id_token);
var tenantId = jwtParsed.tid;
var invocationResult = WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "checkTenant",
parameters: [orgId, tenantId]
});
if (!invocationResult.tenantRegistered) {
return customLoginResponse(true, true, true);
}
}
return authUser(tokenObject, uuid, orgId, ssogroup);
}
And here is the client code:
function azureAuthService($q, _, $state) {
var loginPromise;
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
//first response after protected call
azureChallengeHandler.isCustomResponse = function (response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
return response.responseJSON.realm == realm;
};
//when isCustomResponse returns true
azureChallengeHandler.handleChallenge = function (response) {
WL.Logger.debug("challenge handler -- handleChallenge");
var authRequired = response.responseJSON.authRequired;
var azureTokenRequired = response.responseJSON.azureTokenRequired;
var wrongTenant = response.responseJSON.wrongTenant;
if (wrongTenant) {
loginPromise.reject('wrong tenant');
} else if (authRequired && azureTokenRequired) {
fullLogin();
} else if (authRequired) {
fastLogin();
} else {
loginPromise.resolve();
}
};
azureChallengeHandler.handleFailure = function (error) {
console.log('failure');
console.log(error);
};
return {
init: init,
login: login,
logout: logout
};
function init(config) {
ssogroup = config.ssogroup;
orgId = config.orgId;
}
function login() {
loginPromise = $q.defer();
WL.Client.login(realm, {
onSuccess: function(info) {
loginPromise.resolve();
},
onFailure: function(error) {
loginPromise.reject();
}
});
return loginPromise.promise;
}
function logout() {
var logoutPromise = $q.defer();
var invocationData = {
adapter : 'AzureAuth',
procedure : 'submitLogout',
parameters : [device.uuid, orgId, ssogroup]
};
WL.Client.invokeProcedure(invocationData).then(function () {
WL.Client.logout(realm, {
onSuccess: function () {
logoutPromise.resolve();
},
onFailure: function () {
logoutPromise.reject();
}
});
}, function () {
logoutPromise.reject();
});
return logoutPromise.promise;
}
}
fastLogin and fullLogin is functions that perform some work and finally call
var options = {
parameters: [device.uuid, orgId, ssogroup, transitionAuthObject.requestToken],
adapter: "AzureAuth",
procedure: "submitLogin"
};
azureChallengeHandler.submitAdapterAuthentication(options);
Can't see your fullLogin() and fastLogin() methods so it's hard to say for sure. Make sure that you're calling challengeHandler's submitSuccess() or submitFailure() methods after successful/failed authentication. The authentication framework keeps a queue of requests/responses that require authentication. After successful/failed authentication you need to invoke submitSuccess/submitFailure on challenge handler in order for authentication framework to remove your requests from queue and process it. In case you're not doing so the request remains in the queue and once you're sending a new request that triggers authentication it is put into queue but not handled since there's another request already waiting for authentication.

Worklight - How to check if a client is already logged in

LoginViewModel.js
function login(){
var username='worklight';
var password = 'worklight';
var invocationData = {
adapter : "AuthenticationAdapter",
procedure : "submitAuthentication",
parameters : [ username, password ]
};
adapterAuthRealmChallengeHandler.submitAdapterAuthentication(invocationData, {});
AdapterAuthRealmChallengeProcessor.js
var adapterAuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
adapterAuthRealmChallengeHandler.isCustomResponse = function(response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined'){
return true;
} else {
return false;
}
};
adapterAuthRealmChallengeHandler.handleChallenge = function(response){
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
window.location = "#login";
alert(response.responseJSON.errorMessage);
} else if (authRequired == false){
adapterAuthRealmChallengeHandler.submitSuccess();
window.location = "#Home";
}
};
AuthenticationAdapter-impl.js
function onAuthRequired(headers, errorMessage){
errorMessage = errorMessage ? errorMessage : null;
return {
authRequired: true,
errorMessage: errorMessage
};
}
function submitAuthentication(username, password){
var userIdentity = {
userId: username,
displayName: username,
attributes: {
foo: "bar"
}
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
return {
authRequired: false
};
}
I am not able to set setActiveUser. How to set it and get it. Above wat I have to done for maintaining session. Is it right way to do it. Sometimes I got error Illegal State: Cannot change identity of an already logged in user in realm 'Adapter AuthRealm'. The application must logout first. If I tried to set it null first then it causes my application to enter an infinite loop of attempting to authenticate. Any idea why I would see this behavior?
Anything I am doing wrong?
please help mi out.
Check the SO answer for Worklight logout does not clear active user
Try reloading app onSuccess of Logout.
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){ WL.Client.reloadApp(); },
onFailure: function(){ WL.Logger.debug("Error on logout");}
});
And you can check realm is authenticated or not by using wl client Java script API
WL.Client.isUserAuthenticated("AdapterAuthRealm");