Xero App is connected to multiple organisation - xero-api

I am connecting to Xero from my developer app, looks like Xero has changed some implementation in OAuth2.0 and storing information of apps already connected unlike OAuth1.0 where they allowed to connect to any organisation.
Is there a any way to select only one organisation while connecting to Xero.
enter image description here

So Xero access_tokens for OAuth2.0 are tied to a single user who can potentially authorize multiple orgs (aka tenants). You are correct that OAuth1.0a was a direct 1-1 org to api connection.
If a user authorizes more than one tenant, a few solutions we have seen to solve this UX issue could be:
A dropdown in your app - the user selects which org they want to sync out of their authorized connections. Then you pass that tenantId to your api calls.
In your code, you filter /connections by the updatedDateUtc - and the most recent one is the tenantId you pass to your api calls.
leverage the /disconnect endpoint and highlight in your UI that only one org may be connected at a time.
Ref to docs: https://developer.xero.com/documentation/oauth2/auth-flow
5. Check the full set of tenants you've been authorized to access
You can verify all the tenants that the user has authorized your app to access by calling the connections endpoint. If the user has authorized your app previously, they may have existing tenant connections. All of the connected tenants can now be accessed with your most recent access token.
Each connection will have a created date and an updated date. If they differ, that means the user is reconnecting this tenant to your app (having previosuly connected and disconnected it).
GET https://api.xero.com/connections
Authorization: "Bearer " + access_token
Content-Type: application/json
Response:
[
{
"id": "c869f3b7-6435-4c7e-8cb2-122721b04a69",
"tenantId": "45e4708e-d862-4111-ab3a-dd8cd03913e1",
"tenantType": "ORGANISATION",
"tenantName": "Demo Company (US)",
"createdDateUtc": "2020-02-02T19:17:58.1117990",
"updatedDateUtc": "2020-02-02T19:17:58.1117990"
},
{
"id": "74305bf3-12e0-45e2-8dc8-e3ec73e3b1f9",
"tenantId": "c3d5e782-2153-4cda-bdb4-cec791ceb90d",
"tenantType": "ORGANISATION",
"tenantName": "MY other Sweeet Xero Org",
"createdDateUtc": "2020-01-30T01:33:36.2717380",
"updatedDateUtc": "2020-02-02T19:21:08.5739590"
}
]

Related

Clarification on Google Authentication and Authorization, with OAuth in 2022

I am writing an App and am trying to leverage Google for A&A. The app itself relies on access to the users Google Calendar, and so initially I leveraged their updated OAUTH2 library for A&A.
Here is my flow:
User goes to the index.html which has "https://accounts.google.com/gsi/client" script and google.accounts.oauth2.initCodeClient is called with my client_id, scopes, redirect url
<script src="https://accounts.google.com/gsi/client"></script>
<script>
let client;
function initClient() {
client = google.accounts.oauth2.initCodeClient({
client_id: 'xxxxx-xxxx.apps.googleusercontent.com',
scope:
'https://www.googleapis.com/auth/userinfo.profile \
https://www.googleapis.com/auth/userinfo.email \
https://www.googleapis.com/auth/calendar.readonly \
https://www.googleapis.com/auth/calendar.events',
ux_mode: 'redirect',
redirect_uri: 'http://localhost:5000/oauth2callback',
});
}
// Request an access token
function getAuthCode() {
client.requestCode();
}
The user clicks the login button, which kicks off requestCode() and they begin the login flow. They login or select their google account, then besides the unapproved app screen, they get to the consent screen with my requested scopes.
After, they are redirected to my expressjs endpoint and using the "googleapis" library I exchange with id_token for the access and refresh tokens.
...
const { tokens } = await oauth2Client.getToken(req.query.code); //exchange code for tokens
const userInfo = (
await oauth2Client.verifyIdToken({
idToken: tokens.id_token,
audience: config.google.clientID,
})
).payload;
if (!indexBy.email[userInfo.email]) { // check if user exists
const newUser = {
name: userInfo.name,
email: userInfo.email,
o_id: userInfo.sub,
picture: userInfo.picture,
r_token: tokens.refresh_token,
};
...
Ok, all good.... but not quite. The problem is, that next time the user wants to login to the app, they go through the entire flow again, including the consent screen (again).
So, after going through more docs, even looking at examples from google. I was surprised and I noticed that many of those apps used the passport oauth2 plugin :( Something i've done in the past, but was hoping to avoid that with the recently updated Google client and nodejs libraries.
Ok, how to not prompt for consent screen on subsequent logins?
Maybe separate A&A, so first I use "Sign In With Google" for Authentication, then when I get the user info, check if the user is already registered (hence I have already saved the refresh token) and they start the app.
On the other hand, if they are new (not in existing app user collection), after authenticating, I will then call the OAUTH2 authorization redirect, so again they on Googles site, this time to do the scopes api confirmation.
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed). Hopefully this will still work ok when things come up with expired/invalid refresh token (fingers crossed the default google library handles that).
When doing the Authorize for consent, can I pass something from the previous Authenticate flow so they don't need to do that again.
Or maybe when doing the Authenticate process (Google Identity Service), there is some flag or param so that if they have already consented, they don't have to do that again on subsequent logins.
Incase I wasn't clear, in a nutshell the question is: should I be doing Authenticate for login, separately from Authorization (oauth2 token). Or should I go right into the Authorization flow, which first Authenticates the user, and can I skip the Authorization consent screens if they've already done that. Or maybe there's another way which is the best practice.
Thanks for your attention.
Background info
Authentication is the act where by a user logs in into a system using their login and password. With authentication we know that the user is behind the machine. For this we use Open id connect, which was built on top of Oauth2. Open id connect returns and id_token which can be used to identify the user, it is often a jwt containing some claims to identify the subject or the user behind the Authentication.
The scope used for open id connect is profile and email. open id connect grants you consent to access a users profile information.
This is an example of the decrypted id token returned by google from a simple call using profile scope only. All this id token is telling you is who the user behind the machine is.
{
"iss": "https://accounts.google.com",
"azp": "4074087181.apps.googleusercontent.com",
"aud": "4074087181.apps.googleusercontent.com",
"sub": "1172004755672775346",
"at_hash": "pYlH4icaIx8PssR32_4qWQ",
"name": "Linda Lawton",
"picture": "https://lh3.googleusercontent.com/a-/AOh14GhroCYJp2P9xeYeYk1npchBPK-zbtTxzNQo0WAHI20=s96-c",
"given_name": "Linda",
"family_name": "Lawton",
"locale": "en",
"iat": 1655219027,
"exp": 1655222627
}
In the same call google also returned an access token. Now my call contained only the scope for profile, due to the fact that its open id connect. This means that I will only have access to the data that the profile scope would grant access to. In this case most of what is behind the Google people api.
Note: The user does not see a consent screen with open id connect, even though they are consenting to profile scope. It is assumed by signing into your account that the system you are logging into would have access to your profile info.
Authorization
Authorization is the process by which a user grants your application authorization to access their private user data. The user is shown a consent screen where they consent to your application accessing data defined by some scopes.
In the case of google calendar api there are serval
https://www.googleapis.com/auth/calendar See, edit, share, and permanently delete all the calendars you can access using Google Calendar
https://www.googleapis.com/auth/calendar.events View and edit events on all your calendars
https://www.googleapis.com/auth/calendar.events.readonly View events on all your calendars
https://www.googleapis.com/auth/calendar.readonly See and download any calendar you can access using your Google Calendar
https://www.googleapis.com/auth/calendar.settings.readonly View your Calendar settings
In this case you are only given an access token this is again Oauth2 it is authorization to access the users calendar data it is not authentication this is not related to login.
Your question
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed).
You would do both at the same time.
When you authencation your user make sure to include your google calendar scope then the access token and refresh token returned will grant you access to google calendar.
I am going to assume that you have some kind of user system. When you store the user be sure to store the refresh token that is returned.
As far as Authentication goes i will assume you either have a remember me system which will set a cookie on their machine and remember the user so that you can then get the refresh token from their system the next time they come back.
If they did not chose to select a remember me option then will then have to login every time they visit your site but part of the login will return the "sub": "1172004755672775346", this is the users id on google system so you can use that in your database to match the user when they come back.
Your question is quite complex and will depend upon the type of system you have what it is designed to do as well as what programming language you are using. That being said I hope this very long answer clears things up a bit.

Insufficient Privileges when trying to display user photos on external sites

I have this situation. Im trying to display user photos from an external site, all the configurations about remote sites are already set. I created a connected app in org A and from org B i'm retrieving the users from org A.
All of this works however i cannot show the photos from those users because for that to happen i have to be able to use the ConnectApi.UserProfiles.GetPhoto method which i'm doing right now but i keep getting the "insufficient privileges" error. I tried getting the AccessToken through postman like this
This redirects me to the login site so i can log into my org with my credentials. After that i get an AccessToken.
Next up, i want to be able to get the photos from users from the external site with the AccessToken, However i'm still getting the same error message "Insufficient Priviledges". Am i missing something? thanks in advance
According to: https://sforcenotes.blogspot.com/2015/10/solution-to-display-salesforce-user.html?showComment=1596211057195#c5848226245946017759
The solution is fairly easy but this does not work
I'm bit lost. You have some app that wants to log in to Salesforce and pull images from it? Or do you want to call out from Salesforce and pull images from external app? For first one you likely need "connected app". But you need "remote site settings" or "named credentials" only for calling out.
I'm assuming it's option 1.
There are lots of ways to log in to SF, SOAP API (just username + password), REST API (lots of OAuth2 options, with username+pass or username + JWT or just OAuth2 client id and user logs in to SF interactively, you don't see the password)... Sounds like you're past this stage?
A successful login response will look bit like that (depends on method used):
{"id":"https://login.salesforce.com/id/00Dx0000000BV7z/005x00000012Q9P",
"issued_at":"1278448832702",
"instance_url":"https://yourInstance.salesforce.com/",
"signature":"0CmxinZir53Yex7nE0TD+zMpvIWYGb/bdJh6XfOH6EQ=",
"access_token":"00Dx0000000BV7z!AR8AQAxo9UfVkh8AlV0Gomt9Czx9LjHnSSpwBMmbRcgKFmxOtvxjTrKW19ye6PE3Ds1eQz3z8jr3W7_VbWmEu4Q8TVGSTHxs",
"token_type":"Bearer"}
You're supposed to take from it the access_token (that's your session id. It'll always start with org's id, compare with Setup -> Company information) and instance_url (that's where you're supposed to send any subsequent requests. No more calling the login gateways: login.salesforce.com, test.salesforce.com or mydomain.my.salesforce.com). If you're getting "Insufficient Privileges" I think you didn't change the endpoint.
You can test you logged in OK by sending a GET to the id endpoint you received. It'll give you OpenId info about your user.
Here's my GET to fetch OpenId data with the "Authorization: Bearer " request
So another GET with same header and I have my ugly mug:
If you want somebody else's picture - query similar to /services/data/v48.0/query?q=SELECT SmallPhotoUrl, FullPhotoUrl FROM User WHERE Id = '005...' should work. Check User fields.
{
"totalSize" : 1,
"done" : true,
"records" : [ {
"attributes" : {
"type" : "User",
"url" : "/services/data/v48.0/sobjects/User/(redacted)"
},
"SmallPhotoUrl" : "https://(redacted)/profilephoto/7293L0000008Tfq/T",
"FullPhotoUrl" : "https://(redacted)/profilephoto/7293L0000008Tfq/F"
} ]
}
The key thing is to use the new endpoint and pass the session id in the header. You might even find it easier to use Chatter API to pull photos (also REST-based)

Auth0 suggests the wrong username for ADFS connection

I have an aurelia app configured to auth against auth0, and our auth0 tenant is configured with a connection to our Azure AD instance (which again redirects auth requests to our ADFS server).
The problem I'm seeing is that auth0 seems to remember the wrong username for the connection.
Here's my user:
"upn": "cyxx#domain.com",
"azure_id": "blabla",
"given_name": "Trond",
"family_name": "Hindenes",
"nickname": "cyxx#domain.com",
"tenantid": "1234",
"email": "Trond.Hindenes#domain.com",
As you can see, upn and email are not identical, and AzureAD uses the "upn" for logins. Everything works fine, except that auth0 "remembers" my login based my email address, and when it redirects me to azureAD it pre-fills the username field with my email address, not my upn.
So, my question: Is there any way to force auth0 (either in client settings or the javascript lock library) to send the correct attribute (upn) when redirecting users to AzureAD for logging on?
Currently, when Lock shows the "last time you logged with" message it uses a lastUsedUsername string provided by the server from the getSSOData endpoint as a label to identify the user.
That label is assigned the user.email or, if not available, the user.name, and cannot be changed. I understand that you are ok with this, but just to be clear you cannot alter it (you can remove the message altogether by setting the rememberLastLogin Lock option to false).
Now, if you wish to change the value that is sent to Azure AD, you can use auth.params.login_hint in the options. You can either put an empty string (to prevent Auth0 to send any hint to Azure AD) or put any value that you want (like the user's upn):
var options = {
auth: {
params: {
login_hint: '' // or anything you want
}
}
}
var lock = new Auth0Lock(..., ..., options);
The problem is that, as of now, there's really no good way to get the upn value from the user profile. But if you put an empty string Azure AD will show its own list of last used credentials, which might be good enough for your users.

Power BI - Programmatically sign in

I'm trying to write a web app which embeds some Power BI reports. The data is on-premises so I cannot use the new solution available (Power BI Embedded). Now the inconvenience of using the old approach (https://powerbi.microsoft.com/en-us/documentation/powerbi-developer-integrate-a-power-bi-tile-or-report/) is that the consumer of the web page needs to be a Power BI user which needs to sign in in order for the web app to finally get an authentication token (there is a couple of page redirections that need to happen before).
So my question is, is there a way to do the Power BI Sign In in a programmatic way? so in that way I can just use one Power BI account for getting the content.
I am also experimenting there,
this thread helped me with just that (see post #8):
http://community.powerbi.com/t5/Developer/How-to-use-Power-BI-Rest-API-without-GUI-authentication-redirect/m-p/14218#
Basically:
POST request to:
https://login.microsoftonline.com/common/oauth2/token
Body, form-url-encoded:
grant_type: "password"
scope: "openid"
resource: "https://analysis.windows.net/powerbi/api"
client_id: your client id
client_secret: your client secret
username: that username
password: that usernames password
Then you directly get the token.
Also it might be good to consider security concerns like described here: http://www.cloudidentity.com/blog/2014/07/08/using-adal-net-to-authenticate-users-via-usernamepassword/ under "When NOT to Use This Feature"
It's just a coincidence that I am doing the same. Actually Power BI provides a Rest API to do this very easily. You need to register an app at Azure portal which will provide you with a client id and client secret. Now you can use the rest API and these details.
POST: https://login.microsoftonline.com/common/oauth2/token
and in the body, you need to send these details and in the header, you need to set
Content type : application/x-www-form-urlencoded
data: {
grant_type: password
scope: openid
resource: https://analysis.windows.net/powerbi/api
client_id: {Client ID}
username: {PBI Account Username}
password: {PBI Account Username}
client_secret: {Enter client secret here}
}

Foursquare API - cannot edit venue I am a manager of

I am using the Foursquare API to add/edit venues. I have created a FS user, and created my app. I have done the oAuth token exchange and have my user token.
From here I can make the required API calls to add a venue, get this venue, and then edit this venue. This works brilliantly, like a charm.
I was then made a manager of a different venue. In the FS web front-end, I am able to view this venue, and manage it - i.e. change it's name and so on.
When I then try and change this second venue via the API I get the following response:
{
"meta": {
"code": 403,
"errorType": "not_authorized",
"errorDetail": "User not authorized to edit venue"
},
"response": {}
}
The only difference between the API call that works, and the one that doesn't, is the FS Venue ID (which I know is correct). I'm obviously missing something, but I have no idea what - any thoughts?
The reason seems to be that when you add someone as an owner of the venue via the website, this action only allows this person to manage the Venue via the website. Think of it as the FS website is simply a FS App, like yours or mine.
It might be considered a bit odd if my app could authorise your app to act as manager for one of my users, I suppose.