Webauthn - Windows Hello authenticatorSelection is not working - node.js

I am just developing a sample node js application to play around webauthn on Windows 10.
challenge: challenge,
rp: {
name: "Example CORP",
id : "localhost"
},
user: {
id: new Uint8Array(16),
name: "jdoe#example.com",
displayName: "John Doe"
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7
}
],authenticatorSelection: {
authenticatorAttachment: "platform" //cross-platform is working fine
},
timeout: 60000
};
const credential = navigator.credentials.create({
publicKey: publicKey
});
I do get back the following error and I am not seeing any modal window of Windows Hello.
login:32 publicKey.authenticatorSelection.userVerification was not set to any value in Web Authentication navigator.credentials.create() call. This defaults to 'preferred', which is probably not what you want. If in doubt, set to 'discouraged'. See https://chromium.googlesource.com/chromium/src/+/master/content/browser/webauth/uv_preferred.md for details
Are there any additional params I am missing ?
--
Siva

You didn't define userVerification property in the authenticatorSelection object.
from the W3.org:
Let userVerification be the effective user verification requirement for the assertion:
is set to required
Let userVerification be true.
is set to discouraged
Let userVerification be false.
is set to preferred
If the authenticator
is capable of user verification
Let userVerification be true.
if the authenticator not capable of user verification
Let userVerification be false.
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required"
},

Related

SNS SDK for NodeJS won't create FIFO topic

When I create a topic using the sns.createTopic (like the code below) it won't accept the booleans and say 'InvalidParameterType: Expected params.Attributes['FifoTopic'] to be a string', even though the docs say to provide boolean value, and when I provide it with a string of 'true' it still doesn't set the topic type to be FIFO, anyone knows why?
Here's the code:
const TOPIC = {
Name: 'test.fifo',
Attributes: {
FifoTopic: true,
ContentBasedDeduplication: true
},
Tags: [{
Key: 'test-key',
Value: 'test-value'
}]
};
sns.createTopic(TOPIC).promise().then(console.log);
Used aws-sdk V2
I sent FifoTopic and ContentBasedDeduplication as strings.
The below code works fine for me
const TOPIC = {
Name: 'test.fifo',
Attributes: {
FifoTopic: "true",
ContentBasedDeduplication: "true"
},
Tags: [{
Key: 'test-key',
Value: 'test-value'
}]
};
let sns = new AWS.SNS();
let response3 =await sns.createTopic(TOPIC).promise();
console.log(response3);
Note: Make sure your lambda has correct permissions.
You will be getting attributes like FifoTopic and ContentBasedDeduplication when performing the getTopicAttributes.
let respo = await sns.getTopicAttributes({
TopicArn:"arn:aws:sns:us-east-1:XXXXXXX:test.fifo"}
).promise();
please find the screenshot

How to send Google Calendar invites from node.js using a sendgrid template?

I'm trying to send ics calendar invites to users from node.js server with the goal of getting email clients (e.g. Gmail/Outlook etc) to render them as actual invites and not just usual file attachments.
This is very similar to what Calend.ly does.
So, basically, I'm trying to get something like this in Gmail:
The flow I need is the following (on the client-side):
In my frontend app User 1 presses the schedule event button;
User 2 presses accept button.
The event gets automatically scheduled and appears in both users google calendars (without any OAuth 2.0 stuff or anything like that. Just 2 button presses).
At the same time the users get emails with event details and the .ics file attachment. But the invites should already be in their calendars.
How can I do that?
If I need to use Google Calendar API for this, then how should I at least approach this if I can't have any OAuth 2.0 stuff for my users?
What I'm currently doing is I'm generating .ics files and sending them using SendGrid. However, with .icss I can't achieve a result like in the image above. These .ics files are not invites, they are merely attachments.
So I was wondering how should I approach this at all? Is using Google Calendar API the right way to implement this? If yes, then how can it be done server-side-only without making users authenticate?
I know it's possible because Calendly does exactly this. Users just enter their emails into the input field, press submit and the event invites automatically appear in their Google calendars.
How could this be implemented?
Maybe I don't get something, but generating .ics files doesn't seem to do the trick, and at the same time Google Calendar API does not appear to be the solution as well because of OAuth2 authentication.
In their docs they say:
Your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
Here's the code I'm using to send emails with .ics attachments (there's also a template on SendGrid side, hence the dynamicTemplateData prop):
const SendGrid = require("#sendgrid/mail");
const attachment = {
filename: 'invite.ics',
name: 'invite.ics',
content: Buffer.from(data).toString('base64'),
disposition: 'attachment',
contentId: uuid(),
type: 'text/calendar; method=REQUEST',
};
SendGrid.send({
attachments: [attachment],
templateId,
from: {
email: config.emailSender,
name: config.emailName,
},
to: user.email,
dynamicTemplateData: {
...rest,
user,
},
headers: {
'List-Unsubscribe': `<mailto:unsubscribe.link`,
},
});
And here's how my .ics attachment files look like:
BEGIN:VCALENDAR
PRODID:-//Organization//Organization App//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
DTSTART:20210420T180000Z
DTEND:20210420T190000Z
DTSTAMP:20210418T201735Z
ORGANIZER;CN=Denis Yakovenko:MAILTO:test+1#gmail.com
UID:25bb4d3e-b69d-46b0-baea-489c71c48c88
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Denis Yakovenko;X-NUM-GUESTS=0:MAILTO:test+1#gmail.com
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=John Smith;X-NUM-GUESTS=0:MAILTO:test+2#gmail.com
CREATED:20210418T201735Z
DESCRIPTION:my description
LAST-MODIFIED:20210418T201735Z
LOCATION:https://virtual.location.com
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:my summary
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
I referenced this issue on the sendgrid repository as well as converted the solution from the ruby version to javascript.
The following worked for me successfully and Google generated the event preview.
const ics = require("ics");
const sendgrid = require("#sendgrid/mail");
const event = {
start: [2018, 5, 30, 6, 30],
duration: { hours: 6, minutes: 30 },
title: "Bolder Boulder",
description: "Annual 10-kilometer run in Boulder, Colorado",
location: "Folsom Field, University of Colorado (finish line)",
url: "http://www.bolderboulder.com/",
geo: { lat: 40.0095, lon: 105.2669 },
categories: ["10k races", "Memorial Day Weekend", "Boulder CO"],
status: "CONFIRMED",
busyStatus: "BUSY",
organizer: { name: "Admin", email: "Race#BolderBOULDER.com" },
attendees: [
{
name: "Adam Gibbons",
email: "adam#example.com",
rsvp: true,
partstat: "ACCEPTED",
role: "REQ-PARTICIPANT",
},
],
};
const { value } = ics.createEvent(event);
sendgrid.setApiKey(process.env.SENDGRID_API_KEY);
sendgrid.send({
to: "email#example.com",
from: "test#example.com",
subject: "This is an example email 3",
content: [
{
type: "text/plain",
value: "Plain Content",
},
{
type: "text/html",
value: "HTML Content",
},
{
type: "text/calendar; method=REQUEST",
value: value,
},
],
attachments: [
{
content: Buffer.from(value).toString("base64"),
type: "application/ics",
name: "invite.ics",
filename: "invite.ics",
disposition: "attachment",
},
],
});

Mutable Value in Firebase Push Notification Node.js 10 correct syntax?

I am trying to add Mutable_Content to my push notifications so I can add a badge count.
However I am running into the error:
Messaging payload contains an invalid value for the "notification.mutable_content" property. Values must be strings.
Here is my code for the payload:
const payload = {
notification : {
title: owner + ' has made a post',
body: title + ' - ' + caption,
mutable_content : true
},
};
I have tried many different tutorials to figure out the right syntax but it is not working.
It always comes up with an error.
I am using node.js 10 for the engine and firebase/google cloud platform to trigger the function.
Can anyone help me with this syntax?
Thank you
I had the same question and found unrelated and outdated firebase documentation about it. But you can bypass this by adding a apns field where you can directly put data for the apple notification service. But beware to use the apple syntax with hyphen.
See also: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification
var message = {
notification: {
title: "my notification title",
body: "hello"
},
apns: {
payload: {
aps: {
'mutable-content': true
}
}
},

YubiKey + Webauth: userHandle is always null

When I authenticate using WebAuthn and my YubiKey, the response.userHandle property is always null. That is the user id and displayName that I registered the credential with does not get returned. Is this becuase of something I am doing wrong during the registration / authentication process:
async function register() {
const publicKeyCredentialCreationOptions = {
challenge: Uint8Array.from("this-is-a-test", (c) => c.charCodeAt(0)),
rp: {
name: "Webauthn Test",
id: "localhost",
},
user: {
id: Uint8Array.from("a1b2c3d4e5f6", (c) => c.charCodeAt(0)),
name: "just-a-test",
displayName: "MrUser",
},
pubKeyCredParams: [{ alg: -7, type: "public-key" }],
authenticatorSelection: {
authenticatorAttachment: "cross-platform",
},
timeout: 60000,
attestation: "direct",
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions,
});
}
This is the code I use to authenticate:
async function authenticate() {
const publicKeyCredentialRequestOptions = {
challenge: Uint8Array.from("test", (c) => c.charCodeAt(0)),
allowCredentials: [
{
id: credentialId,
type: "public-key",
transports: ["usb", "ble", "nfc"],
},
],
timeout: 60000,
};
const assertion = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
});
console.log(assertion);
}
What I end up with is:
{
rawId: ArrayBuffer(64),
id: "U-nitqhlORmmdltp7TLO3i18KNoWsSebFyrtc3OIRvcktvwlz-dJZCA1_1gxXrNHzqReU7xGAHdfVP75N2aJSw",
response: {
authenticatorData: ArrayBuffer(37) {}
clientDataJSON: ArrayBuffer(101) {}
signature: ArrayBuffer(71) {}
userHandle: null
}
type: "public-key"
}
As you can see: userHandle is null. Can anyone tell me why?
The userHandle can be null depending on which type of WebAuthn credential the relying party requested to be created.
The default WebAuthn behavior will create a non-discoverable credential and the userHandle returned in the assertion will be null. No data is stored on the authenticator for this type of credential so there is nothing to return.
To create a WebAuthn client-side discoverable credential, a.k.a. resident key, you must set the requireResidentKey member to true. This will store credential data on the authenticator and will return the userHandle in the assertion. Refer to the AuthenticatorSelectionCriteria in the W3C WebAuthn spec for the details.
Here is an example:
authenticatorSelection: {
authenticatorAttachment: "cross-platform",
requireResidentKey: true
},
See Yubico's WebAuthn Dev Guide to learn more about resident keys and the userHandle.
I have tried to understand what you are dealing with. I played with https://u2f.bin.coffee/ to get a feeling for the data flow. As a result of authentication I have received a response like:
Got response:
{
"keyHandle": "F74UNCdNv1d43zw7hqxYgkjR3O6dcevopiSb3jrcB3rMFRUM486LbsVExJD0R3ESC5MCb3zeFGdxvS3ksZ7sCA",
"clientData": "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiTXpPTjhXRHpvSDlhZHU0bTk5YWF0ZyIsIm9yaWdpbiI6Imh0dHBzOi8vdTJmLmJpbi5jb2ZmZWUiLCJjcm9zc09yaWdpbiI6ZmFsc2UsImV4dHJhX2tleXNfbWF5X2JlX2FkZGVkX2hlcmUiOiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ",
"signatureData": "AQAAAAUwRAIgEqi5POKKUraU97W3vbfn34DSWqXwiZwEi5g9QPPtS6MCIBbLYW1_b3aRjHQivSRZQUAfBobx6CZnQ0_VVvuu1LJJ"
}
Now I assume the keyHandle here is your authenticatorData, the clientData here is your clientDataJSON and that signatureData is your signature. Whatever this userHandle is you are missing, it does not seem to be required.
Look at this picture too:
If the userHandle were the handle, the authentication would not work with a null value. But it does if I understand your example correctly.
So I believe you are dealing with a field that is reserved for future purposes or other flows than that which you need at the moment.

Spotify API 1.x get user session

Trying Spotify API 1.x.
My manifest
"Dependencies": {
"api": "1.20.1",
"views": "1.24.1"
}
Having problem getting the current session with the new spotify API. Session Docs
After a while I got the user information with this:
require(['$api/models','$api/models#User','$api/models#Session'], function(models) {
var user = models.User.fromURI('spotify:user:#');
user.load('username', 'name').done(function(u) {
userUid = u.identifier;
});
});
But the Session doesn't have the load method (getting throw error) and when looking at the models.Session I can't se any values??? :(
I changed on manifest.json API version to:
"api": "1.3.0"
Now I can get information from the session, for example my country:
var userCountry = models.session.country;
I find the Spotify documentation a bit misleading regarding Sessions. There are a number of things I have found out by trial-and-error and Googling, rather than from the docs.
Like #Backer says, change the API version to 1.3.0 (or higher, when available). Please note you have to restart Spotify for this to take effect.
Then you can access the Session object like this (here, "session" must be lower case):
models.session.load('product','connection','device','user').done(function(s){
console.log('sess:',s)
});
The User object will be part of this, but it won't be populated with properties unless you load them. Here is an example of retrieving a subset of properties from Session and User:
require([
'$api/models','$api/models#Session'
], function(models) {
app.user = {};
models.session.load('product','connection','device','user').done(function(sess){
sess.user.load('name', 'username', 'subscribed').done(function(user){
app.user.name = user.name; // string
app.user.username = user.username; // string
app.user.subscribed = user.subscribed; // boolean
});
app.user.connection = sess.connection; // string
app.user.country = sess.country; // string ISO-2
app.user.device = sess.device; // string
app.user.language = sess.language; // string ISO-2
app.user.product = sess.product; // string
});
});
The entire Session object:
Session
_done: 65535
_listening: true
_ob: Object
_obcount: 1
_requestArgs: Array[0]
_requestName: "session_event_wait"
capabilities: Object
connecting: false
connection: "wlan"
country: "SE"
developer: true
device: "desktop"
incognito: false
language: "en"
online: true
product: "open"
resolution: 1
streaming: "enabled"
testGroup: 000
user: User
_done: 255
currentUser: true
identifier: "longcodehere"
image: "http://profile-images.scdn.co/artists/default/anotherlongcodehere"
images: Array[2]
name: "My Name"
subscribed: false
uri: "spotify:user:#"
username: "myusername"
__proto__: c
__proto__: c

Resources