Microsoft graph CORS issue

Hi all,
If I could get any help/or advice regarding this CORS issue I’d be very grateful. I’ve done lots of research and can’t quite get my head around the issue (I’m using php and set all the CORS settings as per other posts)

I have a serverflow that uses the Oath2 components for authentication and then makes a GET api call to the Microsoft graph api.
If I just run this file directly in the browser it all works as expected. Oath2 logs in fine, Tokens issued, GET Data returned as expected.
If I try and use this serverflow on another page to provide me with a data source to work with I am receiving CORS header ‘Access-Control-Allow-Origin’ missing error regardless of cors settings or headers added.

In another post it states this as not working because it is being called via ajax, but how else do I use the serverflow to provide the page with a usable datasource?

I understand there is the clientside api datasource but this doesn’t allow for oath2 authentication that the Microsoft Graph api requires does it?
I hope someone can help/enlighten me.
Many thanks Jon

Have you already setup CORS in your project?

Can you post the request/response headers from the network tab of your browser?

I’ve already set up cors in the server settings tab, trying both * and the specific domain to rule that out.
Not at my computer at the moment but the second I am I’ll post the request/response headers
Thanks

Header information from serverconnect mgraph_test.php file

GENERAL
Request URL: https://www.mysite.co.uk/SLSDatabase/dmxConnect/api/mgraph_test.php?
Request Method: GET
Status Code: 302
Remote Address: 217.160.0.190:443
Referrer Policy: strict-origin-when-cross-origin

RESPONSE HEADERS

access-control-allow-credentials: true
access-control-allow-origin: *
cache-control: no-store
content-type: text/html; charset=UTF-8
date: Wed, 15 Mar 2023 15:25:16 GMT
expires: Thu, 19 Nov 1981 08:52:00 GMT
location: https://login.microsoftonline.com//oauth2/v2.0/authorize?blablabla
pragma: no-cache
server: Apache
set-cookie: PHPSESSID=29e353b25b5a40337821d449d41f3fe0; path=/; secure; HttpOnly; SameSite=None
vary: Origin

REQUEST HEADERS

:method: GET
:path: https://www.mysite.co.uk/SLSDatabase/dmxConnect/api/mgraph_test.php?
:scheme: https
accept: application/json
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9,la;q=0.8
cache-control: no-cache
pragma: no-cache
referer: refering page
sec-ch-ua: “Google Chrome”;v=“111”, “Not(A:Brand”;v=“8”, “Chromium”;v=“111”
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: “Windows”
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-origin
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36


Header information from https://login.microsoftonline.com//oauth2/v2.0/authorize?blablabla

Request URL: https://login.microsoftonline.com//oauth2/v2.0/authorize?blablabla
Request Method: GET
Status Code: 200 OK
Referrer Policy: strict-origin-when-cross-origin

RESPONSE HEADERS
Cache-Control: no-store, no-cache
Content-Encoding: gzip
Content-Length: 55308
Content-Type: text/html; charset=utf-8
Date: Wed, 15 Mar 2023 15:25:16 GMT
Expires: -1
nel: {“report_to”:“network-errors”,“max_age”:86400,“success_fraction”:0.001,“failure_fraction”:1.0}
P3P: CP=“DSP CUR OTPi IND OTRi ONL FIN”
Pragma: no-cache
Referrer-Policy: strict-origin-when-cross-origin
report-to: {“group”:“network-errors”,“max_age”:86400,“endpoints”:[{“url”:“https://identity.nel.measure.office.net/api/report?catId=GW+estsfd+ams2”}]}
Set-Cookie: fpc=AlF5FttAntFInfRS1uPq5WU; expires=Fri, 14-Apr-2023 15:25:16 GMT; path=/; secure; HttpOnly; SameSite=None
Set-Cookie: x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly
Set-Cookie: stsservicecookie=estsfd; path=/; secure; samesite=none; httponly
Strict-Transport-Security: max-age=31536000; includeSubDomains
Vary: Accept-Encoding
X-Content-Type-Options: nosniff
x-ms-ests-server: 2.1.14711.7 - NEULR2 ProdSlices
x-ms-request-id: d06dcfc7-bd7d-45ae-9693-9ea975d67800
X-XSS-Protection: 0

REQUEST HEADERS

accept: application/json
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,la;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: login.microsoftonline.com
Origin: https://www.mysite.co.uk
Pragma: no-cache
Referer: https://www.mysite.co.uk/
sec-ch-ua: “Google Chrome”;v=“111”, “Not(A:Brand”;v=“8”, “Chromium”;v=“111”
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: “Windows”
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36


Error message
Access to XMLHttpRequest at ‘https://login.microsoftonline.com//oauth2/v2.0/authorize?blablabla’ (redirected from 'https://www.mysite.co.uk/SLSDatabase/dmxConnect/api/mgraph_test.php?’) from origin ‘https://www.mysite.co.uk’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.


Hope this helps?
If you need any more information just let me know.
I can see there is no access-control-allow-origin: header from https://login.microsoftonline.com//oauth2/v2.0/authorize?blablabla but I have no control over that?

If I run https://www.mysite.co.uk/SLSDatabase/dmxConnect/api/mgraph_test.php? directly it works fine.
Many thanks Jon

You should not use an oauth2 authorization with serverconnect, use the action endpoint directly on a link.

<a href="https://www.mysite.co.uk/SLSDatabase/dmxConnect/api/mgraph_test.php">Login with Microsoft</a>

The authorize step redirects to the Microsoft login page (which can not be loaded using ajax), after login it redirects back to the action page and there you should have an redirect at the end to redirect to the page where you need the oauth2 token.

Thank you for the help, I feel like I’m at getting somewhere at last…
This make sense to me now, it now returns a code in the payload but how do I access/use the token to make an api call that returns me data?

The way OAuth2 works is that you first add an OAuth2 provider under the Globals in Server Actions. You have to configure the endpoints of the service and enter your Client Id and Secret. Default token handling is using the session, this means that the access token and refresh token is stored in a session variable, if you want to use the token for a longer time you probably want to store it in a database.

Next you will need a Server Action for the login, the action will have a OAuth2 Authorize step where you select for which OAuth provider this is and set the correct scopes and params that are needed. After this step you can use the access token for example to get information about the user from an api endpoint. As last step you should add a redirect step. You link to this Server Action on your page using a normal link, normally a button which the user will click. The Authorize step in the action redirects the user to the login page of the OAuth Provider, after login they return back to the Server Action. The Authorize steps checks the query parameters that are returned by the OAuth Provider and calls the token endpoint with the code it received to get the access token. After this you can use the access token to access their API. Redirect the user back to a page on your website.

When the user is authorized you can use the OAuth2 provider to call the API with the access token. You don’t have to enter the access token in the API action, just select OAuth2 as Authorization and select your OAuth provider. When a user was authorized the access token will be added to the header on the API call.

Thank you, that makes alot more sense to me now.
I’ve implemented it in that manner (I think) but am now getting an token empty error
{
“error”: {
“code”: “InvalidAuthenticationToken”,
“message”: “Access token is empty.”,
“innerError”: {
“date”: “2023-03-16T21:04:28”,
“request-id”: “dykjeulkfykdtyjsrjtyjkdtydtydtyj”,
“client-request-id”: “fghdfghdfghdfhmgdtkeyusrjsjs”
}
}
}

when I run the Oath2 authorize script from the on page link it does return a code in the payload
Hopefully this will help show my setup.
(I’ve tried it with and without the Oauth provider on the third image)
Thanks for the help

On the last screenshot - you don’t need the oauth2 provider as a step there. It only needs to be used in the Globals, so remove it.

Also - in the Api Action step there, are you sure you are not missing an authorization header?

As per the Microsoft Graph - Oath2 Get access on behalf of a user - Docs

  1. Register your app with Azure AD.
  2. Get authorization.
  3. Get an access token.
  4. Call Microsoft Graph with the access token.
  5. Use a refresh token to get a new access token.

I seem to be stuck at step 3, I’m getting the correct Authorization response + code (from screenshot 2), but when I try adding in the required headers for the api call (screenshot 3):
tenant, client_id, grant_type, scope, code, redirect_uri, client_secret
I’m still getting an access token is empty error.

do I:
-need a separate api call before the API request to get the token? (as step 3 in microsoft docs)
-or is this handled by wappler and I use the code returned in the authorize step (is this stored in a session variable?) to add to the headers in the API call.

If I do need to, how do I access the code returned from the authorize script?

What puzzles me is if I combine screenshot 2+3 into one script and run it directly I get the expected api response with the data requested so the token is clearly being issued/used if I do it like this?.
Thanks for your time.
Jon

On your screenshot 3 you need to pass this i believe, as per their docs:

After you have an access token, you can use it to call Microsoft Graph by including it in the Authorization header of a request. The following request gets the profile of the signed-in user.

Copy

GET https://graph.microsoft.com/v1.0/me
Authorization: Bearer eyJ0eXAiO ... 0X2tnSQLEANnSPHY0gKcgw
Host: graph.microsoft.com

so you need to add this as a header:

Where the value should be:

'Bearer '+$_SESSION.mic_access_token

Where $_SESSION.mic_access_token is the access token, stored in a session by your mic oauth2 provider.

Really appreciate your help here,
I’ve tried this (and various other approaches and just keep getting “InvalidAuthenticationToken” errors)
If I try and view/echo/print $_SESSION.mic_access_token anywhere I get a blank response from any javascript session storage and/or php session storage.
Many thanks Jon

Hello again!
I feel like I’m getting somewhere to the heart of the issue. Not at all cors related or incorrect API headers/params etc but the session handling in the OAuth2 authorize step (or serverside setup? (standard ionos hosting)).

The problem seems to be that the OAuth2 authorize step is returning a valid access token (visible if I choose to output the step and dont redirect after authorizing), but the only session variable getting created/set once the authorize step is complete is mic_state (or whatever the OAuth Global is called _state).
Access_token + refresh_token aren’t getting created/saved anywhere?
(If I use either session or self-maintain options in th OAuth2 global)

If I turn the redirect step back on and add the mic.access_token variable onto the end of the url it is visible and transfered into the pages url I’m redirected back to.
But if I try and add my own session variable (or cookie) after the authorize step but before the redirect it won’t set any? (regardless of value input - random text or varible from the authorize step)

Any help would be greatfully appreciated, Thanks Jon