I'm new to feathersjs I'm building a backend server using it, and need to add 2 ways of local authentication, by email, or mobile
I've already added new local to the config/default.json as the following
"local-mobile": {
"usernameField": "mobile",
"passwordField": "password"
},
here's my authentication.js file
const { AuthenticationService, JWTStrategy } = require('#feathersjs/authentication');
const { LocalStrategy } = require('#feathersjs/authentication-local');
const { expressOauth } = require('#feathersjs/authentication-oauth');
module.exports = app => {
const authentication = new AuthenticationService(app);
authentication.register('jwt', new JWTStrategy());
authentication.register('local', new LocalStrategy());
authentication.register('local-mobile', new LocalStrategy());
app.use('/authentication', authentication);
app.configure(expressOauth());
};
in src/authentication
But when I send a post request to the authentication endpoint using postman with the following body
{
"strategy":"local-mobile",
"mobile":".......",
"password":"........"
}
the response is coming
{
"name": "NotAuthenticated",
"message": "Invalid authentication information",
"code": 401,
"className": "not-authenticated",
"errors": {}
}
any Idea ?
If you want to allow authentication through the authenticate endpoint, the strategy also has to be added to the authStrategies configuration setting in your config/default.json (feathers-chat example):
{
"authentication": {
// ...
"authStrategies": [
"jwt",
"local",
"local-mobile",
]
}
// ...
}
Related
I'm looking for an answer to best practices for this solution. I have an IOT project im working on that requires authentication on two paths. User and device.
What I am attempting to do is have full control over route authentication separately on user and device so i thought that creating two separate JWT services would be appropriate.
Feathers: 4.5.8
My authentication Service looks like this
export default function (app: Application): void {
const deviceAuthentication = new DeviceAuthService(app, 'device-authentication');
deviceAuthentication.register('device-jwt', new DeviceJWTStrategy());
deviceAuthentication.register('device', new DeviceStrategy());
const authentication = new AuthService(app, 'authentication');
authentication.register('jwt', new JWTStrategy());
authentication.register('local', new LocalStrategy());
app.use('/device-authentication', deviceAuthentication);
app.use('/authentication', authentication);
app.configure(expressOauth());
}
//hooks /someapi
{before:{ get: [authenticate('device-jwt')]}
//config
"device-authentication": {
"entity": "device",
"service": "devices",
"secret": "***",
"authStrategies": [
"device-jwt",
"device"
],
"device": {
"usernameField": "uuid",
"altUsernameField": "email",
"passwordField": "password"
},
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "https://something.com",
"issuer": "feathers",
"algorithm": "HS256",
"expiresIn": "1d"
}
},
"authentication": {
"entity": "user",
"service": "users",
"secret": "*****",
"authStrategies": [
"jwt",
"local"
],
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "https://somthing.com",
"issuer": "feathers",
"algorithm": "HS256",
"expiresIn": "1d"
},
"local": {
"usernameField": "email",
"passwordField": "password"
},
"oauth": {
"redirect": "/",
"auth0": {
"key": "<auth0 oauth key>",
"secret": "<auth0 oauth secret>",
"subdomain": "<auth0 subdomain>"
},
"google": {
"key": "<google oauth key>",
"secret": "<google oauth secret>",
"scope": [
"email",
"profile",
"openid"
]
},
"facebook": {
"key": "<facebook oauth key>",
"secret": "<facebook oauth secret>"
},
"twitter": {
"key": "<twitter oauth key>",
"secret": "<twitter oauth secret>"
},
"github": {
"key": "<github oauth key>",
"secret": "<github oauth secret>"
}
}
},
The problem I am running into is that when i use GET on users, I expect to authenticate on the jwt strategy for the /authentication service. Debugging says i am parsing to the jwt strategy but get a result as see here
Invalid authentication information (strategy not allowed in authStrategies)
the jwt strategy attempted in debug is "device-jwt"
if Swap the service functions to
export default function (app: Application): void {
const authentication = new SippAuthService(app, 'authentication');
authentication.register('jwt', new JWTStrategy());
authentication.register('local', new LocalStrategy());
const deviceAuthentication = new SippDeviceAuthService(app, 'device-authentication');
deviceAuthentication.register('device-jwt', new DeviceJWTStrategy());
deviceAuthentication.register('device', new DeviceStrategy());
app.use('/device-authentication', deviceAuthentication);
app.use('/authentication', authentication);
app.configure(expressOauth());
}
Just the opposite happens, i will get the same message on the users auth service but it will never reach the device auth service
The error is thrown here
///AuthenticationService.authenticate. node_modules/#feathersjs/authentication/src/core.js (204)
if (!authentication || !authStrategy || !strategyAllowed) {
const additionalInfo = (!strategy && ' (no `strategy` set)') ||
(!strategyAllowed && ' (strategy not allowed in authStrategies)') || '';
// If there are no valid strategies or `authentication` is not an object
throw new NotAuthenticated('Invalid authentication information' + additionalInfo);
}
and as expected strategyAllowed is false because "jwt" is not allowed in "device-jwt"
Is there a better way to go about this? Is there a way to utilize express next() functionality to validate the request and pass it to the correct authentication service?
I'm having a hard time getting Auth0 integration working. I'm getting the response shown below
{
"name": "NotAuthenticated",
"message": "error:0909006C:PEM routines:get_name:no start line",
"code": 401,
"className": "not-authenticated",
"data": {
"library": "PEM routines",
"function": "get_name",
"reason": "no start line",
"code": "ERR_OSSL_PEM_NO_START_LINE"
},
"errors": {}
}
When doing a GET to https://localhost:433/users with headers
{
Authorization: Bearer REMOVED
}
The REMOVED part above was the token returned from calling
curl 'http://localhost:3030/users/' -H 'Content-Type: application/json'
Here is my default.json
{
"host": "localhost",
"port": 433,
"public": "../public/",
"paginate": {
"default": 10,
"max": 50
},
"authentication": {
"entity": "user",
"service": "users",
"secret": "REMOVED",
"authStrategies": [
"jwt",
"local"
],
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "http://vice-node-boilerplate",
"issuer": "feathers",
"algorithm": "RS256",
"expiresIn": "1d"
},
"local": {
"usernameField": "email",
"passwordField": "password"
},
"oauth": {
"redirect": "/",
"auth0": {
"key": "REMOVED",
"secret": "REMOVED",
"subdomain": "vicesoftware"
}
}
},
"postgres": "postgres://postgres:#localhost:5432/vice_node_boilerplate"
}
I've updated authentication.js as shown below
const { AuthenticationService, JWTStrategy } = require('#feathersjs/authentication');
const { LocalStrategy } = require('#feathersjs/authentication-local');
const { expressOauth, OAuthStrategy } = require('#feathersjs/authentication-oauth');
class Auth0Strategy extends OAuthStrategy {
async getEntityData(profile) {
const baseData = await super.getEntityData(profile);
return {
...baseData,
email: profile.email
};
}
}
module.exports = app => {
const authentication = new AuthenticationService(app);
authentication.register('jwt', new JWTStrategy());
authentication.register('local', new LocalStrategy());
authentication.register('auth0', new Auth0Strategy());
app.use('/authentication', authentication);
app.configure(expressOauth());
};
and I've updated index.js as shown below
/* eslint-disable no-console */
const https = require('https');
const fs = require('fs');
const logger = require('./logger');
const app = require('./app');
const port = app.get('port');
const key = fs.readFileSync(__dirname + '/localhost-key.pem');
const cert = fs.readFileSync(__dirname + '/localhost.pem');
if (!key) {
throw Error("Unable to read certificate key file");
}
if (!cert){
throw Error("Unable to read certificate cert key file");
}
const server = https.createServer({
key,
cert
}, app).listen(port);
process.on('unhandledRejection', (reason, p) =>
logger.error('Unhandled Rejection at: Promise ', p, reason)
);
server.on('listening', () =>
logger.info('Feathers application started on http://%s:%d', app.get('host'), port)
);
I generated the local dev cert files localhost-key.pem and localhost.pem following the instructions found here: https://auth0.com/docs/libraries/secure-local-development
Edit 1
Note that I also tried generating a cert using these instructions from node.js docs: https://nodejs.org/en/knowledge/HTTP/servers/how-to-create-a-HTTPS-server/
You changed the jwtOptions.algorithm configuration to RS256. This needs additional configuration, specifically a valid private key. The error indicates that the private key provided is not valid. More information can be found in the node-jsonwebtoken documentation. I recommend to stick with the default HS256/384/512 algorithms if you are not sure.
I'm trying to deploy on production (AWS Elasticbeanstalk server) a simple asp net core project that use IdentityServer; my test project is basically the React.js template of Visual Studio 2019 with enabled authentication.
In development all works fine, but in production I have an error when a try to use the jwt token to authenticate to my api.
WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer 'http://***.elasticbeanstalk.com' is invalid"
The access_token used is what was returned from the call
POST http://***.elasticbeanstalk.com/connect/token
The strange behavior is that the following request to
GET http://***.elasticbeanstalk.com/connect/userinfo
It correctly returns the user data, access_token is used here, so I think the token is correct.
Unfortunately, the request to my api fails with the error above.
My Startup.cs code is this:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
The appsetting.json file contains this:
{
"ConnectionStrings": {
"DefaultConnection": "***"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"IdentityServer": {
"Clients": {
"myapp": {
"Profile": "IdentityServerSPA",
"RedirectUris": [ "/signin-oidc" ]
}
},
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "LocalMachine",
"Name": "CN=http://***.elasticbeanstalk.com"
}
},
"AllowedHosts": "*"
}
In your startup set your domain's address
services.AddIdentityServer(options =>
{
options.IssuerUri = "http://***.elasticbeanstalk.com";
})
I'm using Microsoft Graph to connect with Outlook. Can someone please help me with my issue. I need to add more than one ccRecipient and bccRecipient. My web application sends, receives and reads emails through API. But I cant send email to more than one cc and bcc recipient. This is the function i`m using to send email.
edit: Right now function does not have two ccRecipients and two bccRecipients in JSON. I have tried in many different ways but when i test it in microsoft graph-explorer it fails to send on more than one address.
function sendEmail(){
getAccessToken(function(accessToken) {
if (accessToken) {
// Create a Graph client
var client = MicrosoftGraph.Client.init({
authProvider: (done) => {
// Just return the token
done(null, accessToken);
}
});
var recipient = $("#recipient").val();
var subject = $("#subject").val();
var carbon_copies = $("#carbon_copies").val();
var blind_carbon_copies = $("#blind_carbon_copies").val();
var filename_attachment = $("#filename").text();
var attachments_base64 = $("#attachment_base64").val();
var attachments_base64_replaced = attachments_base64.substring(attachments_base64.indexOf(",")+1);
alert(attachments_base64_replaced);
tinyMCE.triggerSave();
var body = $("#moj_tekst_editor").val();
var body_escape_double_qoute = body.replace(/"/g, '\\"');
//var body_escape_single_qoute = body_escape_double_qoute.replace(/'/g, "\\'");
var body_escape_forward_slash = body_escape_double_qoute.replace("/", "\\/");
var body_escape_forward_slash = body_escape_double_qoute.replace("/", "\\/");
alert(body_escape_forward_slash);
var email = '{"message":{"subject": "'+subject+'","body": {"contentType": "HTML","content": "'+body_escape_forward_slash+'"},"toRecipients": [{"emailAddress": {"address": "'+recipient+'"}}],"ccRecipients": [{"emailAddress": {"address": "'+carbon_copies+'"}}],"bccRecipients": [{"emailAddress": {"address": "'+blind_carbon_copies+'"}}],"attachments":[{"#odata.type":"#Microsoft.OutlookServices.FileAttachment","name":"'+filename_attachment+'","contentBytes":"'+attachments_base64_replaced+'"}]}, "saveToSentItems": "true"}'
console.log(email);
// Send Email
client
.api('/me/sendMail')
.header('Content-Type', "application/json")
.post(email, (err, res) => {
if (err) {
callback(null, err);
} else {
callback(res.value);
}
});
} else {
var error = { responseText: 'Could not retrieve access token' };
callback(null, error);
}
});
}
What do I need to do to be able to send email to more than one ccRecipient and bccRecipient? When I add more than one cc recipient message always comes to last one.
Thanks in advance!!
I found I can send emails to multiple toRecipients or ccRecipients by formatting the emailAddress in the following way:
{
"emailAddress": {
"address": "cc1#email.com"
}
},
{
"emailAddress": {
"address": "cc2#email.com"
}
}
Full request body looks like this:
{
"message": {
"subject": "Meet for lunch?",
"body": {
"contentType": "Text",
"content": "The new cafeteria is open."
},
"toRecipients": [
{
"emailAddress": {
"address": "address1#email.com"
}
},
{
"emailAddress": {
"address": "address2#email.com"
}
}
],
"ccRecipients": [
{
"emailAddress": {
"address": "cc1#email.com"
}
},
{
"emailAddress": {
"address": "cc2#email.com"
}
}
]
},
"saveToSentItems": "true"
}
I have the following JSON payload:
"app": {
"name": "myapp",
"version": "1.0.0",
"last_commit": {
"author_name": "Jon Snow"
"author_email": "my#email.com"
}
}
and the following .js file (using Mocha, Supertest and Should):
var supertest = require('supertest')
var should = require('should')
var server = supertest.agent('http://localhost:3001')
describe('GET /', function () {
it('should respond with JSON', function (done) {
server
.get('/')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
.end(function (err, res) {
var payload = res.body.app;
payload.should.have.property("app");
payload.should.have.property("name");
payload.should.have.property("version");
payload.should.have.property("last_commit");
payload.should.have.property("last_commit.author_name");
payload.should.have.property("last_commit.author_email");
done();
});
});
});
When I test the app, I receive the following error message:
Uncaught AssertionError: expected Object {
"name": "myapp",
"version": "1.0.0",
"last_commit": Object {
"author_name": "Jon Snow"
"author_email": "my#email.com"
}
} to have property 'last_commit.author_name'
Why am I receiving an assertion error on the these lines?
payload.should.have.property("last_commit.author_name");
payload.should.have.property("last_commit.author_email");
Assertion is looking for a property called last_commit.author_name which is not present. You may want to break that into two assertions.
payload.should.have.property("last_commit");
let last_commit = payload.last_commit;
last_commit.have.property("author_name");