how do I use SFAuthenticationSession from nativescript? - nativescript

I'm working on some SSO behavour in a nativescript application. I have the following Swift code that works correctly:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var webAuthSession: SFAuthenticationSession?
#IBAction func click(sender: AnyObject)
{
let authURL = URL(string: "http://localhost:5000/redirect.html");
let callbackUrlScheme = "myApp://"
self.webAuthSession = SFAuthenticationSession.init(url: authURL!, callbackURLScheme: callbackUrlScheme, completionHandler: { (callBack:URL?, error:Error?) in
// handle auth response
guard error == nil, let successURL = callBack else {
return
}
print(successURL.absoluteString)
})
self.webAuthSession?.start()
}
}
From this I've come up with the equivalent typescript code (in an .ios.ts file)
function callback(p1: NSURL, p2: NSError) {
console.log('Got into the url callback');
console.log(p1);
console.log(p2);
};
public onTap() {
const session = new SFAuthenticationSession(
{
URL: NSURL.URLWithString('localhost:3500/redirect.html'),
callbackURLScheme: 'myApp://',
completionHandler: callback,
},);
console.log('session created');
console.log('the session is ', session);
console.log('the start method is ', session.start);
console.log('about to call it');
session.start();
console.log('After calling start');
}
This all compiles and builds fine, but when run it crashes at the session.start() call after a second or so delay. I get the output before that, including the 'about to call it' method, but nothing after, not even an error message or stack dump.
Is there anything obvious I'm doing wrong here? Is there anything special that needs to be done to call from typescript to native ios shared library methods?

I think you must store the reference of session variable somewhere globally to the file. Since its scoped local to the function, it may be destroyed as soon the onTap function scope is finished. You could try,
function callback(p1: NSURL, p2: NSError) {
console.log('Got into the url callback');
console.log(p1);
console.log(p2);
};
let session;
public onTap() {
session = new SFAuthenticationSession(
{
URL: NSURL.URLWithString('localhost:3500/redirect.html'),
callbackURLScheme: 'myApp://',
completionHandler: callback,
},);
console.log('session created');
console.log('the session is ', session);
console.log('the start method is ', session.start);
console.log('about to call it');
session.start();
console.log('After calling start');
}

I found the problem today and it's an embarrassingly trivial mistake.
when I did the translation i managed to drop the http:// from the original code.
URL: NSURL.URLWithString('localhost:3500/redirect.html'),
when run on a colleague's machine, we got more details in the log, and it turns out that the only schemes supported are http or https. It must be explicitly added to the url.
so this fixes it (in addition to Manoj's change above)
URL: NSURL.URLWithString('http://localhost:3500/redirect.html'),

Related

Window object is undefined after deploy to netlify

I want to build an email verification. After the user registers, the user gets an email and clicks on it for verification purposes. The email-link invokes a netlify lambda function (api end point). Inside the link is a jwt token, which I decode on the backend. I used
window.location.href
for it and sliced the part I needed and decoded it. On localhost, it works fine, however, if I deploy it to netlify, I get an
window is undefined
error. I read that you have to check for
typeof window !== 'undefined'
However, if I add that to my lambda function I don't get any console.log statements.
exports.handler = async (event, context, callback) => {
if (typeof window !== 'undefined') {
let url = window.location.href
let index = url.indexOf("=");
let token = url.slice(index+1)
console.log(token, 'token here')
const decoded = jwt.verify(token, process.env.SECRET);
console.log('confirm registration route triggered',decoded)
if (decoded) {
const { email } = decoded;
console.log(decoded, 'decoded here')
User.findOneAndUpdate({email: email}, {verified: true },(...e)=>{
console.log(e)
});
} else {
console.log('could not update user')
//redirect user to page with message about email confirmation link expiration
//and proposal to register again
}
console.log('confirm registration got invoked')
}
return {
statusCode: 400,
body: "Oops"
}
};
I read that the function first runs on the server when deployed and afterwards on the client. Seems like it does not run on my client, as I invoke the api-endpoint directly? I'm quite a beginner when it comes to API-Endpoints, thanks for reading!
In case you have the same issue when deploying to netlify, you have to run
event.queryStringParameters
which gives you access to the query parts of your url.

Fitbit URL callback giving a response of NULL

I'm having trouble getting a response from a callback uri and I would really appreciate any help you could give me.
I am trying to use the Fitbit API which requires you to use a callback url to get an Auth Code.
Workflow:
1. Go to Fitbit url to get user to allow the app access to their personal data.
2. User agrees to the conditions
3. User gets redirected to my API
4. The API returns the code from (Code is located in URL and I can access it)
5. I console.log the code out to verify it
6. API returns the code
7. I work with code then exchanging it for an access token.
The problem is that I don't return the code (Or anything )when I return to the app even though I can console.log it on the API. The response I get is NULL
Here is the URL:
url = "https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https://REDIRECT_URL&scope=activity%20heartrate%20location%20nutrition%20profile%20settings%20sleep%20social%20weight&expires_in=604800";
I then open the URL in the InAPPBrowser successfully:
if (url !== "") {
const canOpen = await Linking.canOpenURL(url)
if (canOpen) {
try {
const isAvailable = await InAppBrowser.isAvailable()
if (isAvailable) {
const result =InAppBrowser.open(url, {
// iOS Properties
dismissButtonStyle: 'done',
preferredBarTintColor: 'gray',
preferredControlTintColor: 'white',
// Android Properties
showTitle: true,
toolbarColor: '#6200EE',
secondaryToolbarColor: 'black',
enableDefaultShare: true,
}).then((result) => {
console.log("Response:",JSON.stringify(result))
Linking.getInitialURL().then(url => {
console.log("Tests: ",url)
this._setTracker(url as string);
});
})
} else Linking.openURL(url)
} catch (error) {
console.log("Error: ",error)
}
}
}
From here the URL opens successfully.
Here is the API now which is done in Typescript on AWS serverless and Lambda
export const handler: APIGatewayProxyHandler = async (event, _context, callback) =>{
let provider = event.path
//prints code
let x = event.queryStringParameters
console.log("Code: ",x)
const response = {
statusCode: 200,
body: "Success"
};
return response;
}
Please let me know if further detail is required?
Thank you!
Right so it turns out what I was doing was correct apart from the response should have been 301 which is a redirect response.
const response= {
statusCode: 301,
headers: {
"location": `app://CALLBACK RESPONSE ADDRESS?type=${provider}`
},
body: "Boom"
}

Angular 8 patch request httpclient.subscribe() doesnt hit response, err or ()

I am encountering an issue I do not understand.
Inside one service I have the following code.
when the code hits PATCH LINE, it jumps immediately to RETURN NOTHING LINE.
the catchError line is not hit.
the () line is not hit
the err line is not hit.
I have compared this to working services and I do not see any difference.
patchItem(item_: Project): Observable<Project> {
const url: string = `${this.serviceUrl}/${item_.id}`;
const data = JSON.stringify(item_);
console.log('inside patch item');
console.log(url);
this.http.patch(url, data, httpOptions) //PATCH LINE
.pipe(
catchError(err => this.handleError('PatchItem', err))
)
.subscribe((response) => {
console.log('item patched ');
return this.myResponse.push(response);
}
, err => console.log("inline error encountered")
,() => console.log("this seems to have completed")
);
console.log("return nothing"); //RETURN NOTHING LINE
return null;
}
The API is C# webapi 2
It is being hit.
There is a problem though, I am expecting the JSON to be passed in and webForm_ is always NULL.
This is probably an important clue.
Again, i compare this to working calls and can not find a difference.
When I inspect the variable in jquery, it has the correct value.
In postman, i get the expected response.
[System.Web.Http.HttpPatch]
[Route("{itemId_}")]
public IHttpActionResult PatchById([FromUri] int itemId_, [FromBody] Models.mpgProject webForm_)
{
return JSONStringResultExtension.JSONString(this, "foobar for now", HttpStatusCode.OK);
}
To save a cycle, here is the handleError function.
I have copy/pasted this from a service that is working as expected.
protected handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
How can Angular be skipping all the subscription stuff when it IS actually calling the API?
I think it has to be a simple syntax issue, but I have been staring at it for hours... :-(
Happy to post any other code requested...
tyia
ps - I would also be happy to retitle the question. I am at a loss for how to phrase a good question title for this...
Your handleError method returns a function (that returns an Observable) when it should return an Observable.
It looks as if the error handler you copied from another service does not fit here.
I could imagine an error handler like this:
private handleError<T>(operation = "operation", error: HttpErrorResponse) {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of({} as T);
}
This method returns an Observable of an empty object. However, this might not be the best option to react on a failing HTTP PATCH request. You would better throw that error up to the component and let the user retry.

Parse, JS Updates in Real Time

I have the following code, where I have a myBool (a boolean) in my Data Browser initially set to false,
however sometime while I'm still viewing my page I have code set to turn it to true.
How can I make a real time update that will automatically hide my #div when myBool turns to true?
var myBool = currentUser.get("myBool");
if(myBool) {
$('#div').hide();
}
I did some research and found that the Parse.Cloud.afterSave() function may be useful, but I don't see how it will update the content automatically?
Hope I've been clear!
Thanks.
Edit:
Possibly something like this in my main.js?
Parse.Cloud.afterSave("setBool", function() {
var query = new Parse.Query(Parse.Installation);
query.equalTo('myBool', true);
Parse.Push.send({
where: query,
}, {
success: function() {
$('#div').hide();
},
error: function(error) {
$('#div').show();
}
});
});
Your problem with your afterSave function is that your calling it for a function rather than a class.
AfterSave is called after an object from a certain class is saved. If your bool
Parse.Cloud.afterSave(Parse.Installation, function(request) {
// Send push here, use request to target correct user
});
Additionally your push listener should be the one modifying the divs, not the CloudCode.

KnockoutJS with IE8, occasional problems with Stringify?

A number of our users are still on IE8. Some of them occasionally are reporting problems when trying to post data to our servers (via a big button labeled "SAVE").
There is a script error that IE8 shows, which is: Unexpected call to method or property access, always pointing to the same line in the KnockoutJS 2.2.0 (debug, for now) library, line 450, which is as follows:
return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
The method in my code that is at the root of the stack trace where this happens is this:
self.saveSingle = function (onSuccess, onFailure) {
ko.utils.arrayForEach(self.days(), function (day) {
day.close();
});
var jsonData = ko.toJSON(self);
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: applicationLocation + "/api/assignmentapi/save",
data: jsonData,
success: function (data) {
self.status(data.Status);
self._isDirty(false);
ko.utils.arrayForEach(self.days(), function (day) {
day.clean();
});
if (onSuccess)
onSuccess();
},
error: function (data) {
onFailure();
},
dataType: "json"
});
};
We do strip out a number of properties that are not necessary to our POST as we convert the object to JSON, using this approach: http://www.knockmeout.net/2011/04/controlling-how-object-is-converted-to.html
OurType.prototype.toJSON = function () {
var copy = ko.toJS(this);
delete copy.someUnneededProperty1;
delete copy.someUnneededProperty2;
delete copy.someUnneededProperty3;
delete copy.someUnneededProperty4;
return copy;
}
When it fails, it fails consistently on the line
var jsonData = ko.toJSON(self);
Now here comes the real mess:
It's not consistently happening
It doesn't happen to all IE8 users
We can't consistently reproduce it
The structure of our model that we're serializing doesn't appear matter
The jscript.dll is the current version for IE8
I was also experiencing this issue. Digging deeper I found a few things:
It was only failing occasionally, I found this by running the code in the console
The code in the data-bind was trowing an exception except the message was being swallowed due to IE8 gobbling up the message when using a try {} finally {} block (without catch).
Removing the try finally revealed a cannot parse bindings message.
When I started to get close to figuring out the issue (digging deep into the knockout code) it seemed to disappear in front of my eyes. This is the section of code it was failing on, catching the exception at the end of the code:
ko.utils.extend(ko.bindingProvider.prototype, {
'nodeHasBindings': function(node) {
switch (node.nodeType) {
case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
default: return false;
}
},
'getBindings': function(node, bindingContext) {
var bindingsString = this['getBindingsString'](node, bindingContext);
return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
},
// The following function is only used internally by this default provider.
// It's not part of the interface definition for a general binding provider.
'getBindingsString': function(node, bindingContext) {
switch (node.nodeType) {
case 1: return node.getAttribute(defaultBindingAttributeName); // Element
case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
default: return null;
}
},
// The following function is only used internally by this default provider.
// It's not part of the interface definition for a general binding provider.
'parseBindingsString': function(bindingsString, bindingContext, node) {
try {
var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
return bindingFunction(bindingContext, node);
} catch (ex) {
throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
}
}
});
But yea, it stopped becoming reproducible so I came up with a hack that I tested and works earlier, just retrying the data parsing. So this:
data-bind="value: ko.computed(function(){return ko.toJSON(appViewModel.model()[0])})"
Became this:
data-bind="value: ko.computed(function(){while (true) { try { var json = ko.toJSON(appViewModel.model()[0]); return json; }catch(e){}}})"
Yes, it's very yucky, but it seems to do the trick until our users no longer need IE8 or the Knockout issue is fixed.
I have no idea if this will fix it, but you can use the mapping plugin to go between JS and JSON:
var mapping = {
'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
var viewModel = ko.mapping.toJS(data, mapping);
Taken from my answer to this question
I'd give this a try and see if it helps, as there's nothing obviously wrong in your approach.
Are you sure it's IE8 users who are hitting the issue? IE7 does not support JSON.stringify. You'll need to include the json2.js library to support IE7 and lower.

Resources