webauthn authentication javascript formatting assistance - javascript

I have been trying to figure out how to do 2fa with webauthn and I have the registration part working. The details are really poorly documented, especially all of the encoding payloads in javascript. I am able to register a device to a user, but I am not able to authenticate with that device. For reference, I'm using these resources:
https://github.com/cedarcode/webauthn-ruby
https://www.passwordless.dev/js/mfa.register.js
And specifically, for authentication, I'm trying to mimic this js functionality:
https://www.passwordless.dev/js/mfa.register.js
In my user model, I have a webauthn_id, and several u2f devices, each of which has a public_key and a webauthn_id.
In my Rails app, I do:
options = WebAuthn::Credential.options_for_get(allow: :webauthn_id)
session[:webauthn_options] = options
In my javascript, I try to mimic the js file above and I do (this is embedded ruby):
options = <%= raw #options.as_json.to_json %>
options.challenge = WebAuthnHelpers.coerceToArrayBuffer(options.challenge);
options.allowCredentials = options.allowCredentials.map((c) => {
c.id = WebAuthnHelpers.coerceToArrayBuffer(c.id);
return c;
});
navigator.credentials.get({ "publicKey": options }).then(function (credentialInfoAssertion)
{
// send assertion response back to the server
// to proceed with the control of the credential
alert('here');
}).catch(function (err)
{
debugger
console.error(err); /* THIS IS WHERE THE ERROR IS THROWN */
});
The problem is, I cannot get past navigator.credentials.get, I get this error in the javascript console:
TypeError: CredentialsContainer.get: Element of 'allowCredentials' member of PublicKeyCredentialRequestOptions can't be converted to a dictionary
options at the time navigator.credentials.get is called looks like this:
I've tried every which way to convert my db-stored user and device variables into javascript properly encoded and parsed variables but cannot seem to get it to work. Anything obvious about what I'm doing wrong?
Thanks for any help,
Kevin
UPDATE -
Adding options json generated by the server:
"{\"challenge\":\"SSDYi4I7kRWt5wc5KjuAvgJ3dsQhjy7IPOJ0hvR5tMg\",\"timeout\":120000,\"allowCredentials\":[{\"type\":\"public-key\",\"id\":\"OUckfxGNLGGASUfGiX-1_8FzehlXh3fKvJ98tm59mVukJkKb_CGk1avnorL4sQQASVO9aGqmgn01jf629Jt0Z0SmBpDKd9sL1T5Z9loDrkLTTCIzrIRqhwPC6yrkfBFi\"},{\"type\":\"public-key\",\"id\":\"Fj5T-WPmEMTz139mY-Vo0DTfsNmjwy_mUx6jn5rUEPx-LsY51mxNYidprJ39_cHeAOieg-W12X47iJm42K0Tsixj4_Fl6KjdgYoxQtEYsNF-LPhwtoKwYsy1hZgVojp3\"}]}"

This is an example of the serialised JSON data returned by our implementation:
{
"challenge": "MQ1S8MBSU0M2kiJqJD8wnQ",
"timeout": 60000,
"rpId": "identity.acme.com",
"allowCredentials": [
{
"type": "public-key",
"id": "k5Ti8dLdko1GANsBT-_NZ5L_-8j_8TnoNOYe8mUcs4o",
"transports": [
"internal"
]
},
{
"type": "public-key",
"id": "LAqkKEO99XPCQ7fsUa3stz7K76A_mE5dQwX4S3QS6jdbI9ttSn9Hu37BA31JUGXqgyhTtskL5obe6uZxitbIfA",
"transports": [
"usb"
]
},
{
"type": "public-key",
"id": "nbN3S08Wv2GElRsW9AmK70J1INEpwIywQcOl6rp_DWLm4mcQiH96TmAXSrZRHciZBENVB9rJdE94HPHbeVjtZg",
"transports": [
"usb"
]
}
],
"userVerification": "discouraged",
"extensions": {
"txAuthSimple": "Sign in to your ACME account",
"exts": true,
"uvi": true,
"loc": true,
"uvm": true
}
}
This is parsed to an object and the code used to coerce those base64url encoded values is:
credentialRequestOptions.challenge = WebAuthnHelpers.coerceToArrayBuffer(credentialRequestOptions.challenge);
credentialRequestOptions.allowCredentials = credentialRequestOptions.allowCredentials.map((c) => {
c.id = WebAuthnHelpers.coerceToArrayBuffer(c.id);
return c;
});
Hope that helps. The JSON data is retreived via a fetch() call and the byte[] fields are encoded as base64url on the serverside.

Related

How do I query an index properly with Dynamoose

I'm using Dynamoose to simplify my interactions with DynamoDB in a node.js application. I'm trying to write a query using Dynamoose's Model.query function that will search a table using an index, but it seems like Dynamoose is not including all of the info required to process the query and I'm not sure what I'm doing wrong.
Here's what the schema looks like:
const UserSchema = new dynamoose.Schema({
"user_id": {
"hashKey": true,
"type": String
},
"email": {
"type": String,
"index": {
"global": true,
"name": "email-index"
}
},
"first_name": {
"type": String,
"index": {
"global": true,
"name": "first_name-index"
}
},
"last_name": {
"type": String,
"index": {
"global": true,
"name": "last_name-index"
}
}
)
module.exports = dynamoose.model(config.usersTable, UserSchema)
I'd like to be able to search for users by their email address, so I'm writing a query that looks like this:
Users.query("email").contains(query.email)
.using("email-index")
.all()
.exec()
.then( results => {
res.status(200).json(results)
}).catch( err => {
res.status(500).send("Error searching for users: " + err)
})
I have a global secondary index defined for the email field:
When I try to execute this query, I'm getting the following error:
Error searching for users: ValidationException: Either the KeyConditions or KeyConditionExpression parameter must be specified in the request.
Using the Dynamoose debugging output, I can see that the query winds up looking like this:
aws:dynamodb:query:request - {
"FilterExpression": "contains (#a0, :v0)",
"ExpressionAttributeNames": {
"#a0": "email"
},
"ExpressionAttributeValues": {
":v0": {
"S": "mel"
}
},
"TableName": "user_qa",
"IndexName": "email-index"
}
I note that the actual query sent to DynamoDB does not contain KeyConditions or KeyConditionExpression, as the error message indicates. What am I doing wrong that prevents this query from being written correctly such that it executes the query against the global secondary index I've added for this table?
As it turns out, calls like .contains(text) are used as filters, not query parameters. DynamoDB can't figure out if the text in the index contains the text I'm searching for without looking at every single record, which is a scan, not a query. So it doesn't make sense to try to use .contains(text) in this context, even though it's possible to call it in a chain like the one I constructed. What I ultimately needed to do to make this work is turn my call into a table scan with the .contains(text) filter:
Users.scan({ email: { contains: query.email }}).all().exec().then( ... )
I am not familiar with Dynamoose too much but the following code below will do an update on a record using node.JS and DynamoDB. See the key parameter I have below; by the error message you got it seems you are missing this.
To my knowledge, you must specify a key for an UPDATE request. You can checks the AWS DynamoDB docs to confirm.
var params = {
TableName: table,
Key: {
"id": customerID,
},
UpdateExpression: "set customer_name= :s, customer_address= :p, customer_phone= :u, end_date = :u",
ExpressionAttributeValues: {
":s": customer_name,
":p": customer_address,
":u": customer_phone
},
ReturnValues: "UPDATED_NEW"
};
await docClient.update(params).promise();

Skip Chromecast selection when sending from Google Chrome

I'm trying to send data to a chromecast, but I would like to send the data to a certain Chromecast directly, without selecting it in the Google Chrome.
I would like to skip the Chromecast selection before sending data.
This is want to avoid.
I dont want to select the cast but directly cast the data to it.
I've been checking the session object that we get from chrome.cast.initialize and it return something like this:
{
"sessionId": "b59f1754-fd13-48cd-b237-4952a69cade4",
"appId": "5B797F56",
"displayName": "url-cast-sender",
"statusText": "URL Cast ready...",
"receiver": {
"label": "rTflOUigItAIYPwoZZ87Uv5oK8yI.",
"friendlyName": "Sala de Juntas",
"capabilities": [
"video_out",
"audio_out"
],
"volume": {
"controlType": "attenuation",
"level": 1,
"muted": false,
"stepInterval": 0.05000000074505806
},
"receiverType": "cast",
"isActiveInput": null,
"displayStatus": null
},
"senderApps": [],
"namespaces": [
{
"name": "urn:x-cast:com.google.cast.debugoverlay"
},
{
"name": "urn:x-cast:com.url.cast"
}
],
"media": [],
"status": "connected",
"transportId": "b59f1754-fd13-48cd-b237-4952a69cade4"
};
As you can see there is label there, I've been trying to work with it but nothing.
The way the page request the connection to a chromecast is the following:
// click handlers
document.getElementById('requestSession').onclick = function () {
chrome.cast.requestSession(sessionListener, onErr);
};
Which seems to be the part that opens the selection alert in Google Chrome.
My work is a fork from url-cast-receiver and you can check a demo here.
Turns out it is not possible from the frontend part.
So I ended up using a library called SharpCaster created by Tapanila, in which there is a controller that allows you to do this kind of stuff, here you can find an example of it.
Had some trouble to make it work and also opened an issue in the repository, but ended up fixing it myself, issue #141.
WebPageCastingTester.cs
using System.Linq;
using System.Threading.Tasks;
using SharpCaster.Controllers;
using SharpCaster.Services;
using Xunit;
namespace SharpCaster.Test
{
public class WebPageCastingTester
{
private ChromecastService _chromecastService;
public WebPageCastingTester()
{
_chromecastService = ChromecastService.Current;
var device = _chromecastService.StartLocatingDevices().Result;
_chromecastService.ConnectToChromecast(device.First()).Wait(2000);
}
[Fact]
public async void TestingLaunchingSharpCasterDemo()
{
var controller = await _chromecastService.ChromeCastClient.LaunchWeb();
await Task.Delay(4000);
Assert.NotNull(_chromecastService.ChromeCastClient.ChromecastStatus.Applications.First(x => x.AppId == WebController.WebAppId));
await controller.LoadUrl("https://www.windytv.com/");
await Task.Delay(4000);
Assert.Equal(_chromecastService.ChromeCastClient.ChromecastStatus.Applications.First(x => x.AppId == WebController.WebAppId).StatusText,
"Now Playing: https://www.windytv.com/");
}
}
}

how to access json data contained in a GET response

Continuing yesterday's saga, now I can retrieve json objects in a response but I can't extract the data from them.
The following node.js snippet is from the file "accounts.js" which is in an ETrade api library that exists in the path /lib. It returns json containing data about the accounts of the authenticated user. The authentication part is working great.
exports.listAccounts = function(successCallback,errorCallback)
{
var actionDescriptor = {
method : "GET",
module : "accounts",
action : "accountlist",
useJSON: true,
};
this._run(actionDescriptor,{},successCallback,errorCallback);
};
The ETrade website says this call will produce the following sample response:
{
"AccountListResponse": {
"Account": [
{
"accountDesc": "MyAccount-1",
"accountId": "83405188",
"marginLevel": "MARGIN",
"netAccountValue": "9999871.82",
"registrationType": "INDIVIDUAL"
},
{
"accountDesc": "MyAccount-3",
"accountId": "83405553",
"marginLevel": "CASH",
"netAccountValue": "100105468.99",
"registrationType": "INDIVIDUAL"
},
{
"accountDesc": "SIMPLE IRA",
"accountId": "83405188",
"marginLevel": "CASH",
"netAccountValue": "99794.13",
"registrationType": "IRA"
}
]
}
}
In my app.js file, I have the following:
var etrade = require('./lib/etrade');
var et = new etrade(configuration);
et.listAccounts(
function(res){
var listAccountsRes = res;
console.log('account list success!');
console.log(listAccountsRes)
},
function(error) {
console.log("Error encountered while attempting " +
"to retrieve account list: " +
error);
});
When I run this code, the console log shows the following message:
{ 'json.accountListResponse':
{ response:
[ [Object],
[ [Object],
[ [Object],
[ [Object],
[ [Object],
[ [Object],
[ [Object],
[ [Object] ] } }
Suppose in app.js I want to put the accounts data in a variable called myAccounts.
One of our members, Jack, solved yesterday's problem and when I commented that I still couldn't access the data in the response, he suggested this: "That property has a dot in it so you'll have to use [ ... ] rather than dot notation to access it. See what's inside the objects with a['json.accountListResponse'].response." So far I have not been able to get that to work, even when I use ['json.accountListResponse'].res like this:
var listAccountsRes = [json.accountListResponse].res;
This returns undefined when printed to the console.
Thanks to Adam for his suggestion which led to this which works:
var listAccountsRes = res['json.accountListResponse'];
var listAccounts = listAccountsRes['response'];
console.log('account list success!');
console.log(listAccounts)
Now the console log reports almost exactly what ETrade says I should get. (They appear to have changed the name "Account" to "response"). I presume my variable listAccounts now contains the json with eight sample accounts in it that I can see in my console log. But I still don't know how to access individual elements. There should be some simple code that will iterate over the json file and produce an array of arrays that I could actually use for something. I tried accessing it like an array: console.log(listAccounts[0]) but that returns undefined. Do I need to stringify it or something?

Sending email notifications for Events via Google Calendar API

I'm using the calendar.events.insert API to add an Event to my Calendar via the PHP client.
The event is being inserted correctly along with appropriate values as set by the API.
The same however is not able to trigger an email invite to the attendees. I looked around to find that the request needs to set the param sendNotifications as true.
The same doesn't seem to help either.
Here is a sample code:
var request = gapi.client.calendar.events.insert({
"calendarId" : calendarData.id,
"sendNotifications": true,
"end": {
"dateTime": eventData.endTime
},
"start": {
"dateTime": eventData.startTime
},
"summary": eventData.eventName,
"attendees": jQuery.map(eventData.attendees, function(a) {
return {'email' : a};
}),
"reminders": {
"useDefault": false,
"overrides": [
{
"method": "email",
"minutes": 15
},
{
"method": "popup",
"minutes": 15
}
]
}
});
Where eventData and calendarData are appropriate objects.
Although my main problem is with email invites being sent the first time, I also tried (as can be seen above) to set a reminder (using overrides). While the popup works as expected, I didn't receive an email update in this case either.
This makes me wonder whether this may be a permission issue - something which I need to enable for my app perhaps (the user would understandably need to know if my app is sending emails on their behalf)?
In the Google API Documentation for inserting events, the "sendNotifications" option is actually a parameter. You might want to put it in the request parameters instead of the body.
In Meteor
Note: In my Meteor application, I did did the request by hand, and I'm still new to JavaScript. I'm not sure how you would do that in plain JavaScript or with the calendar API, so I'll just put the Meteor code, hope it helps although it's a bit off-topic.
var reqUrl = "https://www.googleapis.com/calendar/v3/calendars/primary/events";
var payload = {
'headers' : {
'Authorization': "Bearer " + token,
'Content-Type': 'application/json'
},
'params': {
'sendNotifications': true
},
'data': {
"summary": summary,
"location": "",
"start": {
"dateTime": start
},
"end": {
"dateTime": end
},
"attendees": [
{
"email": "*********#gmail.com"
}
]
}
};
Meteor.http.post(reqUrl, reqParams, function () {});
#linaa is correct. Just ran into this issue myself.
In JS, this would look like:
var request = gapi.client.calendar.events.insert(
sendNotifications: true,
{
// request body goes here
}
);
For this you should set the "remindOnRespondedEventsOnly" value to "true".
which means, Whether event reminders should be sent only for events with the user’s response status “Yes” and “Maybe”.
You can find this information here.
Hope that helps!
event = service.events().insert(calendarId='primary', body=event, sendUpdates='all').execute()
this will work

ExtJS - SyntaxError: missing ) in parenthetical

I am writing some code to educate myself in the ways of ExtJS. I am also new to JSON so hopefully this question will be easy for you to answer. I am trying to retrieve some data from a basic web service that I have written which should be returning its results as JSON (seeing as I am new to JSON - it could be that that is broken).
The error I am getting is
SyntaxError: missing ) in
parenthetical
The JSON that I am returning from my web service is
{
"rows": [
{
"id": "100000",
"genre_name": "Action",
"sort_order": "100000"
}, {
"id": "100002",
"genre_name": "Comedy",
"sort_order": "100002"
}, {
"id": "100001",
"genre_name": "Drama",
"sort_order": "100001"
}]
}
My ExtJS code is as below. The loadexception callback is where I have retrieved the JSON and error above from
var genres = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
method: 'POST',
url: 'http://localhost/extjs_training/Demo_WebService/Utility.asmx/GetGenres',
failure: function(response, options){
Ext.get('my_id').dom.innerHTML = 'Load failed: ' + response.status;
}
}),
reader: new Ext.data.JsonReader({
fields: ['id', 'genre_name'],
root: 'rows'
}),
listeners: {
loadexception: function (proxy, options, response, e) {
var result = response.responseText;
Ext.MessageBox.alert('Load failure', e + " ..... " + result);
}
}
});
var loadSuccess = genres.load({
callback: function(r, options, success){
Ext.get('my_id').dom.innerHTML = 'Load status: success=' + success;
}
});
Is the JSON you included above what is actually being returned from the call, or what you are anticipating it should look like? The string you included looks clean, but it looks like you formatted it as well. I'm not sure if the space after "id": is allowed, either. It might not be a big deal, though.
The missing parenthetical typically indicates that something in the JSON is wrong. It could be an extra character before/after the string. Use Firebug to examine what you are getting back, and make sure it is clear of any extra characters.
http://www.sencha.com/forum/showthread.php?10117-Solved-missing-%29-in-parenthetical.
Echoeing two statements was the reason in my case. So check your echoes again.

Categories