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.
Related
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"
},
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
I am testing out the use of "#casl/ability" for RBAC in express.
According to CASL docs, I should be able to define conditional restrictions on attributes against actions upon subjects and in the cases where classes are not used, a subject helper function can be used to wrap DTOs.
reference: https://casl.js.org/v4/en/guide/subject-type-detection
I tried the very simple example below which should have worked. But it does not. Am I understanding it incorrectly in some ways?
import { Ability, subject } from "#casl/ability";
const ability = new Ability([
{
action: "write",
subject: "docs",
conditions: {
publisherId: 53
}
}
]);
const docs = {};
// Also, if the third argument is skipped for 'fields', it throws an error
console.log(
ability.can("write", subject("docs", docs), "", { publisherId: 53 })
);
I have a sandbox here https://codesandbox.io/s/casl-test-conditions-uzc8v?file=/src/index.js:0-286
You incorrectly use ability.can Check the Api docs. That’s why it throws with the error message saying that you incorrectly use can.
To fix your example:
import { Ability, subject } from "#casl/ability";
const ability = new Ability([
{
action: "write",
subject: "docs",
conditions: {
publisherId: 53
}
}
]);
const docs = subject('docs', {
publisherId: 53
}); // “docs” type instance
console.log(
ability.can("write", docs)
);
I'm trying to do a simple transactional 'move' of data from one table to another in DynamoDB (to add indexes). Here's the call I'm making, the promise is returned because I'm using .then() to interpret it, but as you can see from the result I'm getting back there's something wrong with my JSON. Unfortunately, the error message is not very helpful. Very grateful of any help.
var runner = async (column1Value, column2Value) => {
return await ddb.transactWriteItems({
TransactItems: [
{
Delete: {
TableName: 'TABLE_V2',
Key: {
AuthorName: {
S: column1Value
}
}
}
}
,
{
Put: {
Item: {
'AuthorName': {
S: column1Value
},
'AuthorTitle': {
S: column2Value
}
},
TableName: 'TABLE_V3'
}
}
]
}).promise();
}
When I run my code I get the following:
at Request.extractError...
message: 'Transaction cancelled, please refer cancellation reasons for specific reasons [ValidationError, None]',
code: 'TransactionCanceledException',
time: 2019-03-31T00:03:05.401Z,
requestId: 'URIARNJDVFHDVLQQNR1JMIJP5NVV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 30.057548210067033
I've read a few sites out there but all refer to a time froms before DynamoDB natively supported transactions with the .transactWriteItems().
e.g.: How to support transactions in dynamoDB with javascript aws-sdk?
Ok, so, there were two issues (let me know if they are not relevant to this site).
The 'Put' error was being thrown by the fact that there was a space character after the column name for the first column: 'AuthorName '
The 'Delete' error was being thrown by the fact that the TABLE_V2 is using not just a primary key but a search key also, so its JSON needs to be:
Delete: {
TableName: 'TABLE_V2',
Key: {
AuthorName: {
S: column1Value
},
AuthorTitle: {
S: column2Value
}
}
}
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