In other words, to fulfil the Forge Providers requirements, I’d effectively need to authenticate twice when I need to make a remote call – once with a read.User scope to get the profile data from graph, and then again with a ditinct resource-specific scope to fetch crm data.
It appears that this situation is nonstandard compared to all Provider template examples I see offered by Atlassian – they all seem to assume (unless I’m mistaken) that the Profile is just another resource on the server, and you can fetch one token with enough scopes to handle both the Profile call and the subsequent resource call. Is there a way I can develop my Providers section to handle this in my Forge app?
Update: I’ve worked out an implementation, but I have an outstanding question on the most secure pattern to implement this.
First, the implementation –
What I worked out, was to use the provider pattern primarily as a means to call the token issuer (login.microsoft.com) directly in the forge script, essentially so I can override the scope in the providers section of the manifest to one that that is directly intended for the resource server, i.e. https://{orgId}.crm6.dynamics.com/.default :
const microsoft = await api
.asUser()
.withProvider("microsoft", "auth-server");
.
.
.
const response = await microsoft.fetch('/${tenantId}/oauth2/v2.0/token', {
headers: (*auth claims and scope intended for the resource server go here*)
});
const json = await response.json();
const token = json['access_token'];
return token;
I then use the { fetch } method from @forge/api to make the actual requests to the CRM, e.g.
My question is about how best to make the primary token call that precedes each CRM request. Obviously the point of Providers is to implement a safe security model whereby one is not calling the backend directly in the frontend or placing secrets and auth workflows directly in the Forge script, but my workaround has sidestepped this model a little bit. In particular, I don’t see how I can avoid working with sensitive secrets in the frontend with this model (obviously, with the usage of environment variables instead of hardcoding any secrets). What are safe patterns to work with this ‘override’ implementation?