SalesForce canvas app oauth web server flow with minimal user interaction - oauth

So I'm weighing up the pros/cons of the different auth mechanisims for a SalesForce Canvas application.
First, a little background. I've got an existing web app - let's call it "myapp". I setup a VisualForce tab containing a VisualForce page which embeds my canvas app by the following approach:
<apex:page>
<apex:canvasApp applicationName="___________" />
</apex:page>
I first implemented access via the signed request method, which was great, as the user only had to accept my app's permissions on first access, and subsequent attempts to access my canvas app could jump right in to myapp homepage.
From reading this article about packaging canvas apps for different SalesForce editions, signed request method has a limitation:
Most typical Group and Professional Edition customers are not going tobe able to use your Signed Request Connected App unless they upgrade to EE or higher or purchase these features as an add-on.
So I decided to switch to the oauth workflows.
Looking at the user agent oauth flow documentation, it says:
The user must always approve access for this authentication flow. After approving access, the application receives the callback from Salesforce.
This is also not desirable, however the web server oauth flow does not have that requirement - once a user accepts the app's permission requirements, they don't need to be prompted to do that again. It also makes things like adding a "Login with SalesForce" option on myapp's login page super easy to add in.
So I setup the web server oauth flow, and have everything functioning well, and as an added benefit added a "Login with SalesForce" option to my login page - great.
Next step was to set the canvas app in the VisualForce tab up to kick off the web server oauth flow.
The problem I want to solve:
I want to use the web server oauth flow to gain access to the homepage of myapp from that VisualForce tab, with as few user interactions as possible in the process.
Base setup
To initiate the oauth flow, I've setup a URL with the following logic in there to make it simple to craft links that kick off the process from elsewhere. This is used from each of the below examples, and is referenced by links to /salesforce/oauth/......
// Allow links to this page to specify "state" and "prompt" paramaters.
$state = isset($_GET['state']) ? $_GET['state'] : 'login';
$prompt = isset($_GET['prompt']) ? $_GET['prompt'] : '';
// Canvas app contextual information provides the right SalesForce endpoint domain, so provide a way for that to be passed in here, or fallback to standard login.salesforce.com for other workflows.
$authDomain = isset($_GET['authDomain']) ? $_GET['authDomain'] : 'https://login.salesforce.com/services/oauth2/authorize';
$url = $authDomain.'?'.http_build_query(array(
'response_type' => 'code',
'client_id' => 'XXXXXXX_MY_APP_CLIENT_ID',
'redirect_uri' => 'https://'.$_SERVER['HTTP_HOST'].'/salesforce/authorize/',
'state' => $state,
'prompt' => $prompt,
));
header('Location: '.$url);
die;
Failed attempt 1
Using the javascript canvas sdk, redirect the canvas app to initiate my web server oauth flow:
location.href = '/salesforce/oauth?authUrl='+encodeURIComponent(Sfdc.canvas.oauth.loginUrl())+'&state=canvas';
I ran into two problems with this approach:
The canvas app looses the contextual information provided by the #query fragment in it's URL.
X-Frame-Options header of the SalesForce page which has the accept/decline permissions prevent it from displaying within an iframe, even on SalesForce domains.
I feel this would be the best way to accomplish my goal if these issues could be overcome.
I actually did an experiment which resolves issue #1 by loading that URL in another iframe, within my canvas app, and if I'd already accepted myapps permission requirements that worked flawlessly, however I was still stuck with issue #2 when the permissions screen popped up and the whole process failed to complete.
Current solution
I've made the initial canvas app page include a button to click, which opens the web server oauth flow in a new window where it completes successfully. Upon completion, the canvas app iframe is redirected to my app homepage.
I'm unhappy with this, because every time I click on my VisualForce tab, there's a step in there requiring the user to click a button, and a new window pops up to run the oauth workflow. It closes itself automatically with no extra user interaction if the user had already accepted the app permissions in the past, or prompts them to accept if they hadn't yet, and then closes itself.
If I'm stuck with this solution it's not the end of the world - we'll make that button into a splash screen with some marketing crap in there and a big obnoxious "Continue to MyApp" button somewhere.
The question (...finally)
Is there some way I can remove that necessary step of clicking the button every single time the canvas app is loaded, but continue using the web server oauth flow here? (remembering I don't want to use the user agent oauth flow because that has a similar requirement of accepting the permissions every time the user accesses it anyway).
That extra step is particularly annoying when the canvas app is being embedded within a SalesForce account or contact screen - as it stops my app from loading and displaying data to my users until the user clicks on the button.

Either I don't get it, or that's really simple. You need to use oAuth immediate parameter:
immediate — Determines whether the user should be prompted for login and approval. This parameter is optional. The value must be true or false if specified. Default value is false. Note the following:
If set to true, and if the user is currently logged in and has previously approved the client_id, Salesforce skips the approval step.
If set to true and the user is not logged in or has not previously approved the client, Salesforce immediately terminates with the immediate_unsuccessful error code.
Source: 1, 2
The only catch is the last part: you will receive error if user hasn't authorized your app yet. In my experience with oAuth it's really easier to use Javascript to run your requests from the client's browser itself. You can fire off immediate = true request, and right after it immediate = false in case first one failed. Then you send the access_token to your app by creating third request – to your own server.

Related

Azure AD authentication not working as expected for mobile devices

I have built an authentication system for Microsoft Teams tab -( angular application) using Authentication for tabs using Azure Active Directory - Teams and it is working fine on desktop (app and browser) but when I am trying to run on the mobile app and then press Authenticate(seen below in image) button it takes me to the sign-in popup after signing in it returns back to same authenticate page.
Now, when I try it a second time it lands me on the page where I want to but with missing data and also, not as responsive it should be.
The below screenshots taken from the iOS device will help further: I am on Version: 2.4.0
Desktop View:
Major Queries:
Is the Azure AD (https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/authentication/auth-tab-aad) authentication will not work for mobile devices?
If I switch to a Single sign-on(https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/authentication/auth-aad-sso) , are there any limitations to using it and also, will it support all the platforms ?
Is the Azure AD (https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/authentication/auth-tab-aad) authentication will not work for mobile devices?
It should work on mobile devices.
If I switch to a Single sign-on(https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/authentication/auth-aad-sso) , are there any limitations to using it and also, will it support all the platforms ?
Single sign-on doesn't solve anything because if it fails you have to fallback to the default auth flow.
Looks like this is an angular issue, not a Microsoft's auth issue.
The problem is interesting because in general it is working but from the second attempt.
I think it might be the issue with synchronization, when some part of the code is running outside of the Angular but is trying to do something with the variables in the Angular's zone.
I don't know how exactly microsoftTeams.getContext and microsoftTeams.authentication.authenticate work but they are async and if they use setInterval/setTimeout they definitely will work outside of your Angular app.
And if they set data into your angular variables you will have the issues like you described.
So that's what happens in my opinion:
You are clicking auth button, everything is fine you are redirected, authenticated and redirected back to your final page.
On that final page your app is trying to save your token into the local angular variable.
Code that is setting the token works outside the Angular and Angular doesn't see this change. So from the angular's perspective nothing has changed, you are still not authenticated.
Your app is redirecting you to your private page, Angular doesn't see token and redirects you back to the auth page.
<Here something happend, for example ChangeDetectorRef.detectChanges, or other sync things>.
You click auth again and you end up authenticated on your private page, but without the apiKey and UserID (which have the same issue as token)
So to fix this you need to notify angular manually by using ChangeDetectorRef.detectChanges or wrap your async code with the NgZone.run.

PWA freezing on navigation clicks in Safari iOS (13.5) after OpenID Microsoft sign in

PWA Freezing after OpenID authorizes user [iOS Safari Standalone]
I have built a simple PWA for our security staff that allows employees to click links and view content about our company's policies on various matters. The app uses Microsoft's OWIN middleware library to authorize our employees' access into the app via their enterprise Microsoft login creds. When a user clicks the 'Employee Sign in' prompt on our login page, they are redirected to Microsoft's domain to complete the sign-in process. Once sign in is complete, they are redirected back to our app's home page.
The Problem
The problem appears only to arise when iOS users (v13) pin the app to their homescreen and then launch the app in standalone mode, and only after the user has completely terminated the app and then returned. We've tested the app on Chrome, Safari (non-standalone mode), Firefox, and the issue does not present in those browswers. The app functions seamlessly up until the point that the user has completed their Microsoft signin and been redirected back to the home page. At this point, if a user clicks a link to another page (within the app), the app completely locks up, doesn't respond to further button clicks, and doesn't load the page prompted by the user. No errors are thrown in the console.
What we've found immediately kicks everything back into gear is if the user switches to another app (even just for a second) and then switches back to our PWA when it's locked up. At this point, the page that the user attempted to navigate to loads immediately without further prompting and the app works 100% seamlessly after this point. It's only the initial version of the default page that freezes.
Potential Causes
My current working theory is that the problem is being caused by some combination of the following:
Redirection to Microsoft's sign in portal. When the user is sent to Microsoft for auth and then sent back to our domain, there could be issues with session/cookie continuity.
iOS's standalone mode. In conjunction with the above, is it possible that using third-party authentication and briefly leaving the domain of the PWA is causing problems with future page navigation. This is supported by the idea that no other browsers or devices have this issue, and my research suggests that Apple support for PWAs is still in its early stages.
Service worker failure. We have done significant testing to ensure that a service worker is being properly installed and registered when a user first enters the site. We have checks to re-register the SW just in case it is dropped at any point in page navigation. We are confident that at the time a user is redirected back to our home page after authentication that there is an active service worker that handles page GET requests. I have also tested explicitly caching the linked pages accessible from our home page during the service worker's registration to see if serving the page from the cache would alleviate the issue. It did not. This is the code in sw.js that handles fetch requests (taken from Google's handy guide):
// "cache-first" approach for requests from client. Will try to get the file from the cache.
// If no match found, it will send the request onto the network. If both fail serve fallback page.
self.addEventListener("fetch", function (event) {
if (event.request.method !== "GET") return;
event.respondWith(
// Try the cache
caches.match(event.request).then(function (response) {
console.log("[service worker] attempting to fetch file from cache...");
return response || fetch(event.request);
}).catch(function () {
// If both fail, show a generic fallback:
return caches.match(offlineFallbackPage);
})
);
});
I have remotely debugged the PWA in standalone using a Mac, and what I have verified is that the click event that fires when a user clicks a link to navigate to a new page IS being properly handled, so the problem truly appears to lie in the loading of the linked pages themselves. Beyond that, debugging remotely has confirmed that there are no HTTP GET errors (or any other errors) firing at all when attempting to navigate to other pages on the site.
This is the first PWA I've ever built and I'm a novice with all this stuff. So I'd love to know if I'm missing anything or where I can go from here. I've scoured all the forums and can't seem to find answers anywhere. Thanks!
I had a very similar problem in my very specific case. but my pwa (packaged with PwaBuilder) froze on oidc signout, when redirect to applications home url.
In XCode I observed an error stating:
could not signal service com.apple.webkit.webcontent 113 could not find specified service
The problem did not occur with my Identity Provider redirect back, but with the following redirect which initiated the OIDC client library which I am using oidc-client-ts. It turns out that there are two possible ways to set the location/url of a window, assign or setting href. And the library uses assign by default. Changing assign to replace href lead to my iOS PWA not to freeze anymore. Very specific use case but it might help somebody else...
auth.signoutRedirect({
post_logout_redirect_uri: process.env.BASE_URI,
redirectMethod: "replace",
});

Box Application and OAuth Integration: getting back to the client_callback url

I'm working on a Box integration and have run into an interesting scenario:
Box user right-clicks in Box and selects an action
The user is sent in an iframe to the client_url.
Since in box we have to keep track of each users' access and refresh tokens, on this pass we discover in our application logic that the user lacks valid tokens.
As per the docs we send the user to:
GET https://app.box.com/api/oauth2/authorize?response_type=code&client_id=MY_CLIENT_ID&state=security_token%3DKnhMJatFipTAnM0nHlZA
and from there Box sends the user to the configured redirect_uri. My question is after authentication, how do we get back to the confiured client_callback url that we configured for our Box application? I don't see a way of getting back to our original url since we had to inject a view for the Box authentication and are then routed to the oauth callback url.
Thanks!
https://developers.box.com/oauth/
From the Box Support team:
Paul Paulauskas (Box Customer Success)
May 27, 4:26 PM
Hi,
The Box web app integration was never designed around having a full authentication flow. When a web app integration is called, it can deliver an auth_code (it's one of the parameters that you can choose), which can be used to create an access token and a refresh token. This is discussed under the "Popup notification" section of:
https://developers.box.com/box-web-application-integrations/
Let me know if this helps!
Thanks,
Paul
Box Platform Support Engineer
In short, the Box app integration configuration looks like this now:
Note the auth_token attribute is the same as the code attribute returned by the authentication workflow described in https://developers.box.com/oauth/

Primer on Getting Started

I'm just getting started with D2L and am running into problems.
On the "Getting Started" page, I have completed the first three steps:
1) Acquire an App Key/ID pair from D2L - I have received the App ID and App Key
2) Create a test account in your host LMS - I have created a new user account with the administrator role for testing
3) Choose a client library to work with - I am using the PHP SDK
4) Authenticate with your LMS - This is where I'm running into trouble.
When I use the Getting Started sample:
http://samples.valence.desire2learn.com/samples/GettingStartedSample/
And enter my host, app ID and app key and hit on the "Authenticate" button, I get a "This application is not authorized on this LMS instance. Ask your administrator to authorize this application" error.
I am an administrator on my D2L host and I'm not sure how to authorize my own app.
I have tried the following:
Navigating to the "Manage Extensibility" page because that's where D2L says my app should be located, but it isn't there.
Enabling the API (d2l.Security.Api.EnableApi) under the "DOME" page to no avail.
What am I doing wrong?
Based on your question and comments, there were two issues here:
First is that the list of App ID/Key pairs appropriate for your LMS get regularly fetched by your LMS from the D2L KeyTool service. The schedule for this fetching is once a day; accordingly, if the scheduled task isn't set up, or if your LMS isn't identifying itself properly to the KeyTool service, or if time hasn't yet elapsed after key granting to the next scheduled run of the task, the App won't yet be in your LMS' Manage Extensibility list. It sounds like you no longer have that issue.
Second is that the Valence Learning Framework APIs' authentication process (requesting and retrieving a set of user tokens for an LMS user) requires several LMS features to be properly set up: (a) the LMS must be configured to support Deep Linking, (b) the LMS must be set up to handle the ?target= parameter on incoming client URL requests, and curate that parameter throughout the user authentication process.
In cases where your LMS is not doing the user authentication but depending upon another, third-party IDP (like Shibboleth), any ?target= parameter passed into the login process must be taken care of by the IDP and properly handed back to the LMS after user authentication. In a situation where you have multiple redirections occurring during user authentication, this can involve successive generation of a target parameter, and each generation must re-URL-encode the previous request URL in its entirety (like sticking an envelope inside another envelope, inside yet another envelope).
If your LMS is not properly configured to support these two points, which you might not notice during other operations, then client calls to the Learning Framework APIs won't work because the calling client won't be able to fetch back a set of user tokens.
To solve the second of these issues, you may have to contact D2L's Customer Support desk -- they can verify, and adjust as necessary, the LMS configuration part of this authentication chain. If you're integrating your LMS with other third-party IDP components not administered or deployed by D2L, then you might also need to adjust their configurations: D2L can likely advise on what needs to be done there (curate the target parameter on URls), but cannot adjust the configuration for you in those cases.

BrowserField2 - salesforce oauth2 failing at Accept/Deny stage

I have a BlackBerry java-based app that works with salesforce data and uses oauth 2 for authentication. It has been working fine until recently when users have been complaining that once they get to the accept/deny salesforce oauth page, clicking on Accept button displays a salesforce 404 page.
I attached a debugger to the bb simulator (OS7) and found that everything was working fine up to the point where a POST is made to salesforce on pressing the ACCEPT button in the final stage of authentication/authorisation via salesforce oauth2 user agent flow. At this point, the post would succeed and the response body would contain some markup with a small JavaScript block that just performs a document.location.href assignment to force the browser to navigate to a new URL. I noticed that the URL was not what I was expecting - I was expecting to see the redirect_uri callback address + the access token etc data appended to the end of it (as per the sf oauth2 documentation). What I saw instead was a URL that when navigated to leads the user back to a login page with a message saying that they are attempting to access a resource that requires authentication.
If I perform the same flow in say chrome the URL after the post is what I expect. This all used to work just fine so I am confused as to why it would stop working all of a sudden. I've heard that Salesforce changed their oauth 2 implementation back in late 2011 slightly so I wonder if this has broken my client although I don't see how.
As an experiment I removed all of my code except for an embedded BrowserField and made it navigate to the oauth URL as constructed by combining consumer key and various other values as per the documentation and it still fails with a 404 page at the point of clicking on the accept button after a successful login.
If I try the same bootstrap oauth URL in the built in bb browser the full authentication flow works fine and I am eventually redirected to the final callback URL with access token information appended.
Has anyone else out there had trouble with oauth2 user agent flow on BrowserField (from field2 package)?
Have you tried using the "scope=mobile" parameter? This parameter puts you into a very simplified oauth flow that is optimized for blackberry...we've found they generally have trouble getting through the richer flows designed for iOS/Android. That said, I haven't heard of them having issues on BB7...only the 5 and sometimes 6 series
If that fails, could you open a support case with us?
Try also "display=mobile" and "scope=api web" (with a space), depending on what you are trying to access after OAuth.

Resources