I've built out a system where my users can sign HelloSign signatures created with embedded signature request on the backend.
In the frontend, I'm using the hellosign-embedded library and using it like this to open the included iframe:
const helloSignClient = new HelloSign({ clientId: process.env.REACT_APP_HELLO_SIGN_CLIENT_ID });
// ....
const [generateEmbeddedSignUrl] = useMutation<
IGenerateEmbeddedSignUrlResponse,
IGenerateEmbeddedSignUrlVars
>(GenerateEmbeddedSignUrl, {
onCompleted: ({ generateEmbeddedSignUrl }) => {
if (generateEmbeddedSignUrl.error) {
return message.error(
`Error generating signature URL: ${
generateEmbeddedSignUrl.error.userMessage || generateEmbeddedSignUrl.error.description
}`
);
}
if (generateEmbeddedSignUrl.embeddedSignUrl.signUrl) {
// Embedded URL is generated. Start Signing with the Hello Sign SDK
helloSignClient.open(generateEmbeddedSignUrl.embeddedSignUrl.signUrl, {
// testMode will be true on staging or development, false on production
testMode: process.env.NODE_ENV === 'production' ? false : true,
});
}
},
onError: (error) => message.error(error),
});
Now that I have new requirements, I want to place this hellosign signature into a modal, where the modal will have the list next to the iframe (removed some info because sensitive info):
I'm trying to do something like this but it seems that there's some conflicting logic where the hellosign library will create its own modal/iframe and controlled like that. I'm not sure how to override this behavior.
I tried taking my generated sign URL and placing that into its own iframe but I get an error:
Something went wrong!
Your request seems to have been malformed and returned the following error:
→ Missing parameter: client_id
Which I assume is me missing client_id from the library:
const helloSignClient = new HelloSign({ clientId: process.env.REACT_APP_HELLO_SIGN_CLIENT_ID });
I guess my questions are: how do I achieve the mockup that I was given? Is it possible to open an embedded signature link in my own custom modal/iframe? Do I have to take the CSS route and override some behaviors? How do I open an embedded link without needing a client_id or using this library?
Related
I'm having a hard time saving an image that is being picked from Expo (React Native).
https://docs.expo.io/versions/latest/sdk/imagepicker.html
It seems that React Native does not have support for uploading the selected image as blob, but does have a base64 option.
The code:
_pickImage = async () => {
let pickerResult = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
base64: true,
aspect: [4, 4],
});
this._handleImagePicked(pickerResult);
};
_handleImagePicked(pickerResult) {
const uri = pickerResult.base64;
const img = new db.File({ name: 'test.jpg', data: uri, type: 'base64', mimeType: 'image/jpg' });
db.UserData.load(this.state.UserDataID).then(UserData => {
img.upload({ force: true }).then((file) => {
UserData.photo = "https://remarkable-apple-95.app.baqend.com/v1" + file.id;
alert(file.id)
return UserData.update();
},
(error) => { alert(error); }
);
});
}
When I console.log(pickerResult.base64) I get a super long string that looks like base64, but when this is run, the img.upload is throwing the error and it says "PersistentError: An unexpected persistent error occurred."
You're right. React Native has no support for binary data. Unfortunately Baqend does not support base64 file uploads yet.
As a workaround you have 2 options:
Use the React Native Fetch Blob library, which bypasses the limitations of React Native not supporting binary files by uploading and downloading the files directly via native code and gives back a reference to those. Your code could look similar to this:
ImagePicker.showImagePicker(options, async (response) => {
const upload = new db.message.UploadFile('files', 'uploadFetchBlob.jpg')
const body = 'RNFetchBlob-' + response.uri;
RNFetchBlob.fetch('PUT', 'https://{YOUR-APP-NAME}.app.baqend.com/v1' + upload.request.path, upload.request.headers, body).then((res) => {
db.File({ parent: 'files', name: 'uploadFetchBlob.jpg'}).url
})
});
Unfortunately this wont work with the expo client right now, but you'd have to eject your project and use 'native code'.
The second option would be not to use the baqend file endpoint directly, but upload your base64 string to a baqend module instead. There you can parse your base64 string and upload it to your files from within your backend module. You can find an example for this in our Guide. https://www.baqend.com/guide/topics/baqend-code/#handling-binary-data
Hope this helps
We're working with two ember applications that each run different version of ember and ember-simple-auth, and want to get ember-simple-auth to work well with both version.
The old app
Ember 1.8.1
Ember-simple-auth 0.7.3
The new app
Ember 2.3.1
Ember-simple-auth 1.0.1
Uses cookie session store
We trying to change the session API for the older version so that it stores the access and refresh tokens correctly so the new app can use it.
So far, we’ve tried overriding the setup and updateStore methods to work with the authenticated nested object but are still running into issues.
Disclaimer - Patrick Berkeley and I work together. We found a solution after posting this question that I figured I would share.
In order for a 0.7.3 version of ember-simple-auth's cookie store to play nicely with a 1.0.0 version, we did have to normalize how the cookie was being formatted on the app with the earlier version in a few key places, mostly centered around the session object (the 0.7.3 session is an ObjectProxy that can be extended in the consuming app to create your own custom session).
The methods that we needed to override, centered around the structure of data being passed to the cookie store to persist and what was being returned when a session was being restored. The key difference is on version 0.7.3, the access_token, etc is stored top-level on the content object property of the session. With 1.0.0. this is nested inside another object inside content with the property name of authenticated. We therefore needed to ensure that everywhere we were making the assumption to set or get the access_token at the top level, we should instead retrieve one level deeper. With that in mind, we came up with these methods being overridden in our custom session object:
// alias access_token to point to new place
access_token: Ember.computed.alias('content.authenticated.access_token'),
// overridden methods to handle v2 cookie structure
restore: function() {
return new Ember.RSVP.Promise((resolve, reject) => {
const restoredContent = this.store.restore();
const authenticator = restoredContent.authenticated.authenticator;
if (!!authenticator) {
delete restoredContent.authenticated.authenticator;
this.container.lookup(authenticator).restore(restoredContent.authenticated).then(function(content) {
this.setup(authenticator, content);
resolve();
}, () => {
this.store.clear();
reject();
});
} else {
this.store.clear();
reject();
}
});
},
updateStore: function() {
let data = this.content;
if (!Ember.isEmpty(this.authenticator)) {
Ember.set(data, 'authenticated', Ember.merge({ authenticator: this.authenticator }, data.authenticated || {}));
}
if (!Ember.isEmpty(data)) {
this.store.persist(data);
}
},
setup(authenticator, authenticatedContent, trigger) {
trigger = !!trigger && !this.get('isAuthenticated');
this.beginPropertyChanges();
this.setProperties({
isAuthenticated: true,
authenticator
});
Ember.set(this, 'content.authenticated', authenticatedContent);
this.bindToAuthenticatorEvents();
this.updateStore();
this.endPropertyChanges();
if (trigger) {
this.trigger('sessionAuthenticationSucceeded');
}
},
clear: function(trigger) {
trigger = !!trigger && this.get('isAuthenticated');
this.beginPropertyChanges();
this.setProperties({
isAuthenticated: false,
authenticator: null
});
Ember.set(this.content, 'authenticated', {});
this.store.clear();
this.endPropertyChanges();
if (trigger) {
this.trigger('sessionInvalidationSucceeded');
}
},
bindToStoreEvents: function() {
this.store.on('sessionDataUpdated', (content) => {
const authenticator = content.authenticated.authenticator;
this.set('content', content);
if (!!authenticator) {
delete content.authenticated.authenticator;
this.container.lookup(authenticator).restore(content.authenticated).then((content) => {
this.setup(authenticator, content, true);
}, () => {
this.clear(true);
});
} else {
this.clear(true);
}
});
}.observes('store'),
This took us most of the way there. We just needed to ensure that the authenticator name that we use matches the name on 1.0.0. Instead of 'simple-auth-authenticator:oauth2-password-grant', we needed to rename our authenticator via an initializer to 'authenticator:oauth2'. This ensures that the apps with the newer version will be able to handle the correct authenticator events when the cookie session data changes. The initializer logic is simple enough:
import OAuth2 from 'simple-auth-oauth2/authenticators/oauth2';
export default {
name: 'oauth2',
before: 'simple-auth',
initialize: function(container) {
container.register('authenticator:oauth2', OAuth2);
}
};
The above satisfies our needs- we can sign in to an app using ember-simple-auth 0.7.3 and have the cookie session stored and formatted properly to be handled by another app on ember-simple-auth 1.0.0.
Ideally, we would just update the Ember and Ember Simple Auth versions of the app though business needs and the fact that we wanted to focus our energies on the v2 versions (which are completely fresh and new code bases) propelled us to go down this path.
I'm pretty new to sails, but after read the doc and followed some examples at the Internet, I decided to give it a shot ;)
I have made an APP that depend on a REST webservice that I want to build in Sails Framework - but after a lots of research I haven't found the right solutions in sails yet.
I think I want to pass a (username, password) or a api_key in each webservice call made from the app?
All the examples that i found was only with a session login method - not with an API key in each call.
I used this tutorial - http://jethrokuan.github.io/2013/12/19/Using-Passport-With-Sails-JS.html
But only logins at post to login page - I want it to login in every call and still want to use the build in REST API blueprints.
The problem in my solution is that a call to like this - will not give me all the users as expected because of the default REST method - I want it to auth the user and give me the result ..
http://example.com:1337/user/?username=test&password=xxx
What is the "best practises" for building a APP with a REST webservice backend? - "with sails"
Some of my auth code:
// policies/authentication.js
if(req.param('username') && req.param('password')) {
UserAuth.auth(req, res, function(err, user) {
if (err) return res.forbidden('You are not permitted to perform this action.');
if(user) {
return next();
}
});
}else{
return res.forbidden('You are not permitted to perform this action.');
}
// services/UserAuth.js
module.exports = {
auth : function(req, res, cb) {
var bcrypt = require('bcrypt');
var passport = require("passport");
passport.authenticate('local', function(err, user, info){
if (err) return cb({ error: 'auth error!', status: 400 });
if(user) {
cb(null, user);
}
})(req, res);
}
}
// config/policies.js
module.exports.policies = {
'*': "authentication"
};
First off, it's bad practice to continuously expose usernames and passwords in the wild like this. At the very least, you should consider issuing access_tokens that expire after some time, and need to be re-issued via a login system.
Second, if you want to authenticate on every request (instead of using sessions), it's better to do so using a request header, rather than putting the credentials in the query string. This is especially true when using Sails blueprints; otherwise you'll have to do extra work to keep the blueprints from using your credentials as search criteria.
When using a header, per-request authorization becomes simple with Sails. Set up a policy in api/policies called (for example) auth.js:
module.exports = function (req, res, next) {
// Find an access header
var accessToken = req.header('my-auth-header');
// No header, no access
if (!accessToken) {return res.forbidden();}
// Find the user with that token
User.findOne({accessToken: accessToken})
.exec(function(err, user) {
// Handle error
if (err) {return next(err);}
// Handle bad access token
if (!user) {return res.forbidden();}
// Handle success
return next();
});
}
Then you can set any controller actions that need authentication using the config/policies.js file:
module.exports = {
SomeController: {
'*': 'auth'
},
...etc...
}
Is there a way in Ember.js (and Ember-data) to send credentials to an api that requires Basic HTTP Authentication? I can see how it's done in JQuery here, but don't see any straightforward way to do it in Ember. I thought maybe adding something to the header would work (see below in coffeescript), but no success:
App.AuthAdapter = DS.RESTAdapter.extend(
host: 'https://my-api.example.com'
namespace: 'v1'
headers:
"Authorization Basic fooUsername:barPassword"
...
You can extend the default Rest adapter and add a headers hash which will be included in the ajax that's sent.
App.ApplicationAdapter = DS.RESTAdapter.extend(
headers:
withCredentials: true
Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
)
Or you could take it a step farther and override the ajax method
App.ApplicationAdapter = DS.RESTAdapter.extend(
ajax: (url, type, hash) ->
adapter = this
new Ember.RSVP.Promise((resolve, reject) ->
hash = hash or {}
hash.url = url
hash.type = type
hash.dataType = "json"
hash.context = adapter
if hash.data and type isnt "GET"
hash.contentType = "application/json; charset=utf-8"
hash.data = JSON.stringify(hash.data)
if adapter.headers isnt `undefined`
headers = adapter.headers
hash.beforeSend = (xhr) ->
forEach.call Ember.keys(headers), (key) ->
xhr.setRequestHeader key, headers[key]
hash.success = (json) ->
Ember.run null, resolve, json
hash.error = (jqXHR, textStatus, errorThrown) ->
Ember.run null, reject, adapter.ajaxError(jqXHR)
Ember.$.ajax hash
)
)
Can you use $.ajaxPrefilter? e.g.
Ember.$.ajaxPrefilter (options) ->
options.xhrFields = { withCredentials: true }
options.username = 'fooUsername'
options.password = 'barPassword'
true # need to return non-falsy here
As #gerry3 stated $.ajaxPrefilter is a valid solution.
But if you want to solve a problem of dynamically changing your Headers AFTER an event, for instance, a successful LOGIN attempt, then you need to put more wires. In my case I need to send back a 'Token' Header that is provided by the server after a successful AJAX-login. But, of course, when the user initiates the App he's not logged-in already.
The problem is that once you reopen or extend the RESTAdapter, or define an ajaxPrefilter, even if you're binding it to a value (or localStorage as in my case) the class won't be following the current variable value. It's like a snapshot taken at some moment. So it's useless in my scenario.
I'm following Embercast Client Authentication which is a good start (code available), but instead of jQuery data-fetching I'm using Ember-Data.
So the trick is to observe the token and re-define the ajaxPrefilter as many times as you need it.
tokenChanged: function() {
this.get('token')=='' ?
localStorage.removeItem('token') :
localStorage.token = this.get('token');
$.ajaxPrefilter(function(options, originalOptions, xhr) {
return xhr.setRequestHeader('Token', localStorage.token);
});
}.observes('token')
Therefore, when the user logs-in he'll have a valid token and send it in every request to the server via the RESTAdapter.
Hope this helps someone.
Consider this Ember JS Model:
App.User = DS.Model.extend({
firstName: DS.attr('string')
});
I am able to successfully save the model on the server using this as an XHR request:
{
"user": {
"first_name":"dude"
}
}
but for some reason it gives me an error while returning this XHR response:
{
"id":1,
"user":{
"first_name":"dude"
},
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
The error says: Your server returned a hash with the key id but you have no mapping for it
Ember expects the output to look like:
{
"user": {
"id":1,
"first_name":"dude",
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
}
I think the problem lies in the request itself, but I'm not sure.
Note that I'm using the Sails API as my backend.
You can use a controller to marshal the data format to whatever you need-- but this raises an interesting question about adding support for different front-end conventions to the API blueprints. Right now, Sails.js API blueprints support Backbone out of the box, but obviously that doesn't do you a lot of good if you're using Ember :) I created an issue for that here https://github.com/balderdashy/sails/issues/317.
Here's a hacky example of how you'd use a custom controller to send back data in this format using Sails today:
// api/controllers/UserController.js
module.exports = {
// Create action: (e.g. using default route, you'd POST to /user/create)
create: function (req,res) {
// Grab attributes from request using Ember conventions
var newAttributes = req.param('user');
// Create the user object in the datastore
User.create(newAttributes, function (err, newUser) {
// If there was an error, handle it
if (err) return res.send(err,500);
// Respond with the user object using Ember conventions
res.json({
user: newUser
});
});
}
};
That's a weirdly formatted JSON response. Do you have access to the server?
Ember expects the response as a a hash with root keys
{
"user": {
"id":1,
"first_name":"dude",
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
}