How to match AppRoleAssignments with applications in "Graph explorer API" - azure-active-directory

How to match https://graph.microsoft.com/beta/me/appRoleAssignments with https://graph.microsoft.com/beta/applications.
Which ID should be matched?
How to compare these two JSON code -
{
"id": "cLsrKP9FQU-3yUaE6gaYwgT2qe43q4pAqMb4Kr9Cdp4",
"creationTimestamp": "2019-04-08T06:17:53.349594Z",
"appRoleId": "00000000-0000-0000-0000-000000000000",
"principalDisplayName": "<User Name>",
"principalId": "282bbb70-45ff-4f41-b7c9-4684ea0698c2",
"principalType": "User",
"resourceDisplayName": "Postman",
"resourceId": "d24064b4-1ee0-4507-a220-6faab7ba3fe0"
},
With
{
"id": "b5bb2bb9-bb5e-426a-a107-d2212020f614",
"deletedDateTime": null,
"isFallbackPublicClient": false,
"appId": "c21feb4a-040e-4067-8c14-55b1e015fc17",
"applicationTemplateId": null,
"identifierUris": [
"https://<OrgName>.onmicrosoft.com/5d959b28-00fd-4f67-8d14-1a6276919b28"
],
"createdDateTime": "2019-02-27T07:33:40Z",
"displayName": "Postman",
"isDeviceOnlyAuthSupported": null,
"groupMembershipClaims": null,
"optionalClaims": null,
"orgRestrictions": [],
"publisherDomain": "<OrgName>.onmicrosoft.com",
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null,
"api": {
"requestedAccessTokenVersion": null,
"acceptMappedClaims": null,
"knownClientApplications": [],
"oauth2PermissionScopes": [
{
"adminConsentDescription": "Allow the application to access Postman on behalf of the signed-in user.",
"adminConsentDisplayName": "Access Postman",
"id": "2e9e3ada-8570-4e8a-b02b-f0822f4fd63c",
"isEnabled": true,
"type": "User",
"userConsentDescription": "Allow the application to access Postman on your behalf.",
"userConsentDisplayName": "Access Postman",
"value": "user_impersonation"
}
],
"preAuthorizedApplications": []
},
I want to filter out the apps assigned to me from the master list of applications.
Which ID should be matched?
Edited-
I am getting 403 error while calling this API through Web-Part - https://graph.microsoft.com/beta/applications
private _getListApplications(param): Promise<any> {
return this.context.aadHttpClientFactory
.getClient('https://graph.microsoft.com')
.then((client: AadHttpClient) => {
return client
.get("https://graph.microsoft.com/beta/applications", AadHttpClient.configurations.v1);
}).
then((responseListAllApps: SPHttpClientResponse) => {
return responseListAllApps.json();
});
}
{
"error": {
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
"innerError": {
"request-id": "a0cae64d-ae22-47a3-a765-3abe2b1c34a1",
"date": "2019-04-08T09:23:25"
}
}
}

No, there is no ID matched directly, you could not do that via these two APIs. You need to use GET https://graph.microsoft.com/beta/servicePrincipals/xxxxxxxxx as a medium.
First, call GET https://graph.microsoft.com/beta/me/appRoleAssignments, copy the resourceId in the response, it is the object id of the target resource (service principal) for which the assignment was made. Then call GET https://graph.microsoft.com/beta/servicePrincipals/<resourceId>, the appId in the response is the application id of the AD App. Then call GET https://graph.microsoft.com/beta/applications, the appId in the response is the same with the appId retuned by GET https://graph.microsoft.com/beta/servicePrincipals/<resourceId>. Then you can match them.
For more details about the properties, see these three links:
https://learn.microsoft.com/en-us/graph/api/resources/approleassignment?view=graph-rest-beta#properties
https://learn.microsoft.com/en-us/graph/api/resources/serviceprincipal?view=graph-rest-beta#properties
https://learn.microsoft.com/en-us/graph/api/resources/application?view=graph-rest-beta#properties

Related

Blazor WASM AzureAD IdentityServer results in AADSTS90023: Public clients can't send a client secret

I'm trying to get IdentityServer 6 in Blazor WASM (an Asp.Net hosted solution) to get to work with a Microsoft id but am running into the error message "AADSTS90023: Public clients can't send a client secret". I feel like I've tried every configuration I can think off but hopefully I still missed something.
The IdentityServer configuration uses the 'SPA' profile which I believe is correct for my scenario:
"IdentityServer": {
"Clients": {
"Blazor.Client": {
"ClientId": "Blazor.Client",
"ClientName": "Blazor.Client",
"Profile": "SPA",
"RedirectUri": "https://localhost:15601",
"LogoutUri": "https://localhost:15601"
}
}
}
The configuration code follows the simplest example:
.AddMicrosoftAccount(options =>
{
options.ClientId = <clientId>;
options.ClientSecret = <secret>
})
Because these code snippets are basically the simplest way of doing this I'm assuming something is wrong with my AzureAD application registration but I can't figure out what. I've included the manifest:
{
"id": "<id>",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": false,
"appId": "<apiId>",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2021-12-10T09:21:08Z",
"certification": null,
"disabledByMicrosoftStatus": null,
"groupMembershipClaims": null,
"identifierUris": [
"api://<apiId>"
],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "<name>",
"oauth2AllowIdTokenImplicitFlow": false,
"oauth2AllowImplicitFlow": false,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2023-12-10T09:21:45.315Z",
"keyId": "<keyId>",
"startDate": "2021-12-10T09:21:45.315Z",
"value": null,
"createdOn": "2021-12-10T09:21:57.2927675Z",
"hint": "H1f",
"displayName": "<displayName>"
}
],
"preAuthorizedApplications": [],
"publisherDomain": "<publisherDomain>.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://localhost:15602/signin-microsoft",
"type": "Spa"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "<someId>",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [],
"tokenEncryptionKeyId": null
}
Is this scenario not supported or am I doing something wrong?
EDIT: ultimately the problem lied in the redirect uri platform which should not be set to 'SPA' but to 'Web' because it isn't the client doing the authentication but the IdentityServer web service. The relevant part would be:
"replyUrlsWithType": [
{
"url": "https://localhost:15602/signin-microsoft",
"type": "Web"
}
The
client secret actually must be kept secret, i.e; you cannot put it in the website
and use it from a public front-end .Client credentials flow design says the same.
Blazor webassembly applications are called a 'public
application' in oAuth/openid terms.
Note: According to microsoft docs:
Public clients (native applications and single page apps) must not use
secrets or certificates when redeeming an authorization code - always
ensure that your redirect URIs correctly indicate the type of
application
So try to disable the requirement for the client secret on the IdP and refresh tokens since they can't also be handled in a secure/safe way.
The recommendation is that you use code+PKCE for public clients,
which happens automatically when you set the response_type to code.
Reference: Microsoft identity platform and OAuth 2.0 authorization code flow - Microsoft identity platform | Microsoft Docs

Excel Add-In keeps getting 401 error when trying to access Dynamics 365 Web Api

I am trying to develop a word add in in React with Typescript that allows me to access the dynamics 365 Web Api. The best example I've actually found was in a sample excel add in that accesses the graph api using MSAL 2.0 here https://github.com/OfficeDev/PnP-OfficeAddins/tree/master/Samples/auth/Office-Add-in-Microsoft-Graph-React. If I can get a successful get request to my Dynamics 365 api after modifying the example code I will port the code over to my Word Add in.
However I keep getting a 401 error when trying to go to https://saltrial.crm.dynamics.com/api/data/v9.1/
I get a response back when I simply paste this in a browser, but inside the add in I get 401 unauthorized. I also get 401 unauthorized when I paste the token received from the add in into postman with the header of Bearer + token. I have gotten a successful Access token however when I selected
I will show you my setup in Azure AD.... I have a client secret setup, but am not using it in my add in code.
Manifest
{
"id": "35c3a758-0edb-45f6-a97d-9c7180decd73",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": true,
"appId": "4f7xxxxxxxxxxxxxxxxxxxxxx310bf0",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2021-01-24T07:09:12Z",
"disabledByMicrosoftStatus": null,
"groupMembershipClaims": null,
"identifierUris": [
"api://localhost:3000/4fxxxxxxxxxxxxxxxxxxxxxxx"
],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "TrySSO",
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [
{
"adminConsentDescription": "Enable Office to call the add-in's web APIs with the same rights as the current user.",
"adminConsentDisplayName": "Office can act as the user",
"id": "5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f",
"isEnabled": true,
"lang": null,
"origin": "Application",
"type": "User",
"userConsentDescription": "Enable Office to call the add-in's web APIs with the same rights that you have.",
"userConsentDisplayName": "Office can act as you",
"value": "access_as_user"
}
],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2299-12-31T05:00:00Z",
"keyId": "570axxxxxxxxxxxxxxxxxxxxxxxxxxbcd1",
"startDate": "2021-01-28T04:11:05.086Z",
"value": null,
"createdOn": "2021-01-28T04:11:06.027737Z",
"hint": "mm-",
"displayName": "wordaddinsecret"
}
],
"preAuthorizedApplications": [
{
"appId": "ea5a67f6-b6f3-4338-b240-c655ddc3cc8e",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "d3590ed6-52b3-4102-aeff-aad2292ab01c",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "57fb890c-0dab-4253-a5e0-7188c88b2bb4",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
},
{
"appId": "bc59ab01-8403-45c6-8796-ac3ef710b3e3",
"permissionIds": [
"5b3a4e4a-e55e-45ba-820b-ea16efbe3d5f"
]
}
],
"publisherDomain": "salnewtrial.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://localhost:3000/login/login.html",
"type": "Spa"
},
{
"url": "https://login.microsoftonline.com/common/oauth2/nativeclient",
"type": "InstalledClient"
},
{
"url": "https://localhost:3000/login.html",
"type": "Web"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000007-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "78ce3f0f-a1ce-49c2-8cde-64b5c0896db4",
"type": "Scope"
}
]
},
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "14dad69e-099b-42c9-810b-d002981feec1",
"type": "Scope"
},
{
"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
"type": "Scope"
},
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null
}
Inside my login.ts in excel add in
(() => {
// The initialize function must be run each time a new page is loaded
Office.initialize = () => {
const msalInstance = new PublicClientApplication({
auth: {
clientId: "4f7f40ec-xxxxxxxxx-5d6b18310bf0",
authority: "https://login.microsoftonline.com/cd77a053-xxxxxxxxxx3402c0fd62", *This is tenant id
redirectUri: "https://localhost:3000/login/login.html", // Must be registered as "spa" type
},
cache: {
cacheLocation: "localStorage", // needed to avoid "login required" error
storeAuthStateInCookie: true, // recommended to avoid certain IE/Edge issues
},
});
// handleRedirectPromise should be invoked on every page load
msalInstance
.handleRedirectPromise()
.then((response) => {
// If response is non-null, it means page is returning from AAD with a successful response
if (response) {
Office.context.ui.messageParent(
JSON.stringify({ status: "success", result: response.accessToken })
);
} else {
// Otherwise, invoke login
msalInstance.loginRedirect({
scopes: [
"user.read",
"files.read.all",
"https://saltrial.crm.dynamics.com//user_impersonation",
],
});
}
})
.catch((error) => {
const errorData: string = `errorMessage: ${error.errorCode}
message: ${error.errorMessage}
errorCode: ${error.stack}`;
Office.context.ui.messageParent(
JSON.stringify({ status: "failure", result: errorData })
);
});
};
})();
Here is my API Call in Add in
import axios from 'axios';
export const getGraphData = async (url: string, accesstoken: string) => {
const response = await axios({
url: url,
method: 'get',
headers: {'Authorization': `Bearer ${accesstoken}`,
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
"Accept": "application/json",
"Content-Type": "application/json; charset=utf-8",
}
});
return response;
};
Code that calls my api function and passes the scopes and dynamics url. I console log the access token and put it into postman with header Bearer token, I get 401. Also the add-in displays 401 unauthorized in the task pane....Ignore any naming of functions referring to the graph api, I'm hitting the dynamics 365 api and hoping I don't get the 401 error. Thanks.
getFileNames = async () => {
this.setState({ fileFetch: "fetchInProcess" });
getGraphData(
// Get the `name` property of the first 3 Excel workbooks in the user's OneDrive.
"https://saltrial.crm.dynamics.com/api/data/v9.1/WhoAmI",
this.accessToken
)
.then(async (response) => {
await writeFileNamesToWorksheet(response, this.displayError);
this.setState({ fileFetch: "fetched", headerMessage: "Success" });
})
.catch((requestError) => {
// If this runs, then the `then` method did not run, so this error must be
// from the Axios request in getGraphData, not the Office.js in
// writeFileNamesToWorksheet
console.log("Access Token >>>>>>>>>>>>>>>>> ", this.accessToken);
this.displayError(requestError);
});
};

Grant admin consent - granted consent not taken into account

I have a JavaScript application, using MSAL.js 1.3.0. I'm prompted with the login screen and I'm able to login. But no matter how I configure the application in the Azure Portal, I'm always receiving thethe following message after I've logged in:
So, I granted the application admin consent: (which wasn't necessary for delegated permissions couple of weeks ago. Additionally, the page below doesn't show 'Admin consent required' at all)
But still, the behaviour doesn't change. I'm not able to get past the "need admin approval" message.
My coding is here:
config: {
msalConfig: {
auth: {
clientId: "b273e5a6-8940-481b-9c87-1b51310ff88c"
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true
}
},
graphBaseEndpoint: "https://graph.microsoft.com/v1.0/",
userInfoSuffix: "me/",
queryMessagesSuffix: "me/messages?$search=\"$1\"&$top=150",
scopeConfig: {
scopes: ['User.Read', 'Mail.Read']
}
},
onInit: function () {
this.oMsalClient = new Msal.UserAgentApplication(this.config.msalConfig);
//check if the user is already signed in
if (!this.oMsalClient.getAccount()) {
this.oMsalClient.loginPopup(this.config.scopeConfig).then(this.fetchUserInfo.bind(this));
} else {
this.fetchUserInfo();
}
}
Here's my application manifest:
{
"id": "HERE-WOULD-BE-AN-ID",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": null,
"appId": "b273e5a6-8940-481b-9c87-1b51310ff88c",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2020-06-09T10:31:12Z",
"groupMembershipClaims": null,
"identifierUris": [],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "2020-06-09 - Fiori Sandbox Test2",
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [],
"preAuthorizedApplications": [],
"publisherDomain": "d063937domain.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://d063937trial-d063937trialb-devspace-msgraph-ui.cfapps.eu10.hana.ondemand.com/ui/index.html",
"type": "Web"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "570282fd-fa5c-430d-a7fd-fc8dc98a9dca",
"type": "Scope"
},
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [],
"tokenEncryptionKeyId": null,
"verifiedPublisher": {
"displayName": null,
"verifiedPublisherId": null,
"addedDateTime": null
}
}
PS: I'm not a JavaScript developer, just took the recently working app from a colleague and wanted to use it. If anything is missing, please let me know.
Cheers,
Max
If you want end users can consent to apps accessing company data on their behalf, you need to log into Azure Portal by using an admin account to enable this feature.
Go to Azure Active Directory -> Enterprise applications -> User settings, select Yes for "Users can consent to apps accessing company data on their behalf".
If you want end users can request access to applications that require admin consent, you could configure the admin consent workflow.
I had a similar issue. It was because I did not have an MPN ID in my App Registration. (MPN => Microsoft Partner Network)
The result was that Users (even Admin) would be redirected automatically but concent just didnt work.

AAD not providing refresh_token when requesting offline_access

For using Microsoft Graph in our application, I need a token for a specific user. I retrieve a bearer token form Azure Active Directory, but it is missing a refresh_token. It appears that the offline_access permission is being removed from the scopes.
The body of the request I'm sending to the /token endpoint is:
{
code=*somecode*,
client_id=*my clientid*,
client_secret=*my clientsecret*,
redirect_uri=http://localhost:8080/msgraph/callback,
scope=profile Calendars.Read User.ReadBasic.All email User.Read offline_access Calendars.Read.Shared openid,
grant_type=authorization_code
}
I do not use implicit grants. The endpoint I connect to is V2.
In the App registration, I have added all the permissions from the sent scope.
In the token response, all of the requested scopes are returned, except for offline_access. The refresh_token is left empty but I do not receive any errors.
The manifest from the MSGraph administration is
{
"id": "xxxx",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": null,
"appId": "xxxxx",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2018-06-07T07:53:26Z",
"groupMembershipClaims": null,
"identifierUris": [],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "xxxxx",
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2099-12-31T12:00:00Z",
"keyId": "xxxxx",
"startDate": "2018-06-07T07:58:18.4289954Z",
"value": null,
"createdOn": "2018-06-07T07:58:21.1632167Z",
"hint": "gpi",
"displayName": null
}
],
"preAuthorizedApplications": [],
"publisherDomain": "xxxx.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "http://localhost:8080/msgraph/callback",
"type": "Web"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "xxxxx",
"resourceAccess": [
{
"id": "64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0",
"type": "Scope"
},
{
"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
"type": "Scope"
},
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "14dad69e-099b-42c9-810b-d002981feec1",
"type": "Scope"
},
{
"id": "465a38f9-76ea-45b9-9f34-9e8b0d4b0b42",
"type": "Scope"
},
{
"id": "2b9c4092-424d-4249-948d-b43879977640",
"type": "Scope"
},
{
"id": "570282fd-fa5c-430d-a7fd-fc8dc98a9dca",
"type": "Scope"
},
{
"id": "7b9103a5-4610-446b-9670-80643382c1fa",
"type": "Scope"
},
{
"id": "f45671fb-e0fe-4b4b-be20-3d3ce43f1bcb",
"type": "Scope"
},
{
"id": "88d21fd4-8e5a-4c32-b5e2-4a1c95f34f72",
"type": "Scope"
},
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
},
{
"id": "b340eb25-3456-403f-be2f-af7a0d370277",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": "http://localhost:8080/",
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [
"guidedSetupCreatedOrUpdated",
"appModelVersion:2",
"accessTokenVersion:1",
"availableToOtherTenants:true",
"supportsConvergence:true"
],
"tokenEncryptionKeyId": null
}
The request body you posted above isn't in application/x-www-form-urlencoded. Since you're getting an access_token, I'm assuming this is just what you're providing to whichever library you're using to make the actual HTTP request. Just in case, the actual request body should look something like this (line breaks for readability):
grant_type=authorization_code&
code=[AUTHORIZATION CODE]&
client_id=[APPLICATION ID]&
client_secret=[PASSWORD]&
scope=[SCOPE]&
redirect_uri=[REDIRECT URI]
The values your sending looking correct to me with one exception, you shouldn't be passing prompt=consent at this stage.
The prompt parameter is used to force the user to consent during the interactive 1st phase of the flow, not when retrieving the token in the 2nd. It shouldn't result in the behavior you're experiencing but "shouldn't" and "doesn't" are often not the same thing.
If, however, you've added offline_access after the user has consented and you're only passing prompt=consent at this stage, it is possible your app has not received consent for offline_access yet. The token response will only provide scopes that have been consented.
If the user hasn't consented to one of the requested scopes, it will simply not provide that permission rather than returning an error. This allows your app to compare the scopes returned to the scopes requested and allow you to determine if you need to request a reconsent.
To understand why this is/how this works, imagine you request both User.Read.All and User.ReadBasic.All. Since User.Read.All requires Admin Consent, it is possible for a user to login to your app before administrative consent has been granted. In that case, your app may fall back to using User.ReadBasic.All as an alternative. The only difference to the user would be the data you show them would be limited (i.e. maybe your app wants to show displayName and birthday but can fall back to only displayName).
I have analyzed the response from Microsoft today and now it contains the refresh_token! It seems that the is some delay since it did'nt work thursday but works now on monday.
The returned scope in the response still doesn't contain offline_access but the refresh_token parameter is now deliverd.
Thanks for your contributions!

How to identify Azure AD cloud-based applications returned by Microsoft Graph API /beta/applications endpoint?

I have a work account in Azure AD which has been granted with access for some cloud based applications within an organisation. I can login, see and start the applications from the MyApps portal.
I created a simple Javascript (React) application that needs to read the same list of applications. I'm fetching it from Microsoft Graph API /beta/applications endpoint.
How do I filter the list to keep only the cloud based apps shown in MyApps portal?
I successfully implemented the following:
Authentication using MSAL against Azure AD.
Fetched the list of all applications within user's organisation from Microsoft Graph API (GET https://graph.microsoft.com/beta/applications). I can see the same list in Azure Portal (Azure Active Directory -> App registrations (Preview)) for the specified organisation which is named here "directory".
This is an application item returned by Microsoft Graph API and shown in MyApps portal:
{
"id": "xxxxxxxx-xxxx-xxxx-xxxx-28a2179502fc",
"deletedDateTime": null,
"isFallbackPublicClient": false,
"appId": "xxxxxxxx-xxxx-xxxx-xxxx-987d1f301c21",
"identifierUris": [ "https://bitbucket.xxxxxxxxxx.io/plugins/servlet/samlsso", "http://samlssoforbitbucketbyresolutionXXXXXXXXXXXXXXXXXB9CCD803EA52CD5", "http://instanceid_xxxxxxxx-xxxx-xxxx-xxxx-19ba3d89b76f_XXXXXXXXXXXXX342A36A84E328233FB4", "http://samlssoforbitbucketbyresolution/xxxxxxxx-xxxx-xxxx-xxxx-e3b08c22b8e9" ],
"createdDateTime": "2017-12-02T09:05:35Z",
"displayName": "Bitbucket",
"isDeviceOnlyAuthSupported": null,
"groupMembershipClaims": "SecurityGroup",
"optionalClaims": null,
"orgRestrictions": [],
"publisherDomain": null,
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null,
"api": {
"requestedAccessTokenVersion": null,
"acceptMappedClaims": null,
"knownClientApplications": [],
"oauth2PermissionScopes": [
{
"adminConsentDescription": "Allow the application to access SAML SSO for Bitbucket by resolution GmbH on behalf of the signed-in user.",
"adminConsentDisplayName": "Access SAML SSO for Bitbucket by resolution GmbH",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-f434ba7b017f",
"isEnabled": true,
"type": "User",
"userConsentDescription": "Allow the application to access SAML SSO for Bitbucket by resolution GmbH on your behalf.",
"userConsentDisplayName": "Access SAML SSO for Bitbucket by resolution GmbH",
"value": "user_impersonation"
}
],
"preAuthorizedApplications": []
},
"appRoles": [
{
"allowedMemberTypes": [ "User" ],
"description": "msiam_access",
"displayName": "msiam_access",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-2c6355321d55",
"isEnabled": true,
"origin": "Application",
"value": null
}
],
"publicClient": { "redirectUris": [] },
"info": {
"termsOfServiceUrl": null,
"supportUrl": null,
"privacyStatementUrl": null,
"marketingUrl": null,
"logoUrl": "https://secure.aadcdn.microsoftonline-p.com/c1c6b6c8-ugrfrtjv0so5ofkk2h6reh-pajssvvffd-ccwghwns/appbranding/ovibbplm5xqvqcq-zht1z7zxk3gr-vyq3raaci-yoj4/1033/bannerlogo?ts=636727159738380290"
},
"keyCredentials": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [],
"requiredResourceAccess": [],
"web": {
"redirectUris": [ "https://bitbucket.xxxxxxxxxx.io/plugins/servlet/samlsso", "https://*.lab.resolution.de/plugins/servlet/samlsso" ],
"homePageUrl": "https://*.lab.resolution.de/plugins/servlet/samlsso?metadata=samlssoforbitbucketbyresolution|ISV9.2|primary|z",
"logoutUrl": null,
"implicitGrantSettings": {
"enableIdTokenIssuance": true,
"enableAccessTokenIssuance": false
}
}
}
I would have been expected a flag or type field but I couldn't find any to differentiate the items.
This link is documenting MyApps portal.
And here is the documentation of the endpoint.
I will be grateful for any help to bring some light on this.

Resources