Code Monkey home page Code Monkey logo

Comments (19)

JonasKs avatar JonasKs commented on June 19, 2024 1

Yeah, I've never actually tried this.

It seems from the link you sent that:

In the code snippet above, even though the user consents to both User.Read and api://<myCustomApiClientId>/My.Scope scopes, they will only receive an Access Token for MS Graph API, in accordance with per-resource-per-scope(s) principle. However, since they already consented to api://<myCustomApiClientId>/My.Scope, they can acquire an Access Token for that resource/scope silently later on.

I believe, that this means the same would work the opposite way, where if you selected your application scope (user_impersonation) first, and then User.Read, you could authenticate as normal. This access token would then have already been consented to read the graph API. You can then use this access token to fetch another access token, which you then can use for the Graph API.

I have a packed day today, but I'll see if I find some time tonight or this weekend. 😊

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024 1

Just got granted admin consent now and everything works here too!

Thanks for troubleshooting this with me! I'll make sure to add some helper functions and documentation on how this works.

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024 1

Thank you @h3rmanj ❤️

Add the Swagger app registrations Client ID to the knownClientApplications array in the manifest

I initially thought I messed up this step, but I had both the swagger app client ID and the Graph client ID in there. So not sure why it didn't work for me yesterday. Maybe updating manifests are slow (like they can be with v1 -> v2 swap)? I never attempted again today before I got admin consent.

Anyway, on a fresh app registration everything worked as you described.

I'll write some docs. 🎉

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

Hi!

I’m afraid not. I still haven’t used the Graph API.

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

If anyone has used the Graph API and would like to contribute to the documentation with a little tutorial, that would be greatly appreciated. 🚀

from fastapi-azure-auth.

u-iandono avatar u-iandono commented on June 19, 2024

I got the following error when trying to call the Microsoft Graph API using the access token coming from the User object:

{'error': {'code': 'InvalidAuthenticationToken', 'message': 'Invalid x5t claim.', 'innerError': {'date': '2022-03-31T03:16:06', 'request-id': 'ee81c3f0-f21
d-44d6-a7bd-c91bdd102971', 'client-request-id': 'ee81c3f0-f21d-44d6-a7bd-c91bdd102971'}}}

After reading this: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md, I realized it's related to how the scopes work in AzureAD.

So I tried adding the User.Read scope to both the Backend and OpenAPI apps, and then only selecting this scope in the login pop-up in the OpenAPI docs. But I got the following response:

{
  "detail": "Unable to validate token"
}

And this is the error stack:

Invalid token. Error: Signature verification failed.
Traceback (most recent call last):
  File "c:\users\me\.virtualenvs\x-lru6flmw\lib\site-packages\jose\jws.py", line 262, in _verify_signature
    raise JWSSignatureError()
jose.exceptions.JWSSignatureError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\me\.virtualenvs\x-lru6flmw\lib\site-packages\jose\jwt.py", line 142, in decode
    payload = jws.verify(token, key, algorithms, verify=verify_signature)
  File "c:\users\me\.virtualenvs\x-lru6flmw\lib\site-packages\jose\jws.py", line 73, in verify
    _verify_signature(signing_input, header, signature, key, algorithms)
  File "c:\users\me\.virtualenvs\x-lru6flmw\lib\site-packages\jose\jws.py", line 264, in _verify_signature
    raise JWSError("Signature verification failed.")
jose.exceptions.JWSError: Signature verification failed.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\me\.virtualenvs\x-lru6flmw\lib\site-packages\fastapi_azure_auth\auth.py", line 183, in __call__
    token = jwt.decode(
  File "c:\users\me\.virtualenvs\x-lru6flmw\lib\site-packages\jose\jwt.py", line 144, in decode
    raise JWTError(e)
jose.exceptions.JWTError: Signature verification failed.

I don't know how to proceed from this point 😅

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

A token with the wrong audience (for Graph) will always be denied, and that's what you see when you get this error:

{
  "detail": "Unable to validate token"
}

Pasting the token into jwt.io will show you that the aud is 0000... and not your client ID, and must be denied by our token validation.

The token with the correct aud can not work for the Graph API, for the same reason. This is where the on-behalf-flow(OBO) comes in. As far as I can understand, we should use the scope api://{settings.APP_CLIENT_ID}/.default when prompting the user, and then fetch another token like this:

async with AsyncClient() as client:
    data = {
        'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'client_id': settings.APP_CLIENT_ID,
        'client_secret': settings.GRAPH_SECRET,
        'assertion': request.state.user.access_token,
        'scope': 'https://graph.microsoft.com/user.read',
        'requested_token_use': 'on_behalf_of'
    }
    response = await client.post('https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/token', data=data)

with the secret created for the backend appreg.

However, this still gives me an error:

{'error': 'invalid_grant', 'error_description': "AADSTS65001: The user or administrator has not consented to use the application with ID '<application id>' named 'FastAPI-Azure-Auth-DemoProj'. Send an interactive authorization request for this user and resource.", 'error_codes': [65001], 'suberror': 'consent_required'}

which I don't understand, since I consent to that on login, and can confirm by checking the Enterprise Application for the OpenAPI appreg:

image

The token does not have the User.Read scope in it, even when using .default as the scope:
image

Any tips or tricks we should check out @h3rmanj ?

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

I also tried adding

	"knownClientApplications": [
		"00000003-0000-0000-c000-000000000000"
	],

to my manifests, but didn't help :/

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

I pushed a graph debug branch if someone want to look over the code and give it a shot themselves. I still haven't been able to make it work.

Click for instructions
  1. Copy demo_project/.env.example -> demo_project/.env
  2. Fill out the .env values
  3. poetry install to install dependencies
  4. uvicorn demo_project.main:app --reload --host localhost --port 8000 to run the project
  5. Navigate to localhost:8000/docs and click Authorize
  6. Scroll to the top and use the SingleTenant authentication method.
  7. Select the scope and leave client secret blank. Click authorize
  8. Sign in
  9. Call the hello-graph endpoint

To attempt different scopes, see azure_scheme in demoproject/api/dependencies.py.

from fastapi-azure-auth.

u-iandono avatar u-iandono commented on June 19, 2024

@JonasKs I got this result

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6IkNndDNnRDhidDYxZ18yUkpvZmZFSmhnMlRUZHhoa2E3Q3JLUVNMcWNGVjAiLCJhbGciOiJSUzI1NiIsIng1dCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyIsImtpZCI6ImpTM",
  "token_type": "Bearer",
  "scope": "profile openid email https://graph.microsoft.com/User.Read",
  "expires_in": 4431,
  "ext_expires_in": 4431
}

Does this mean success?

EDIT:

I tried calling the Microsoft Graph API with the token and, yeah, I got the result.
image

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

Using OBO? 🎉

How's your appregs set up? I'd love to make it work for me too so I can write docs for it (PRs welcome!😊)

Edit: I removed your access token from your message.

from fastapi-azure-auth.

u-iandono avatar u-iandono commented on June 19, 2024

Using OBO? 🎉

How's your appregs set up? I'd love to make it work for me too so I can write docs for it

Yeah 🎊 , I only change this part from your code

response = await client.post('https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/token', data=data)

I use the tenant ID of Backend API appregs instead of the OpenAPI

I think the rest of the configuration is the same.

image

Edit: I removed your access token from your message.

Thank you so much!

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

Alright, thank you! I don’t have that green check box after User.Read so I’ll have to look into that.

You’re saying your appregs are the exact same as my tutorial told you to set them up? 😊

from fastapi-azure-auth.

u-iandono avatar u-iandono commented on June 19, 2024

Alright, thank you! I don’t have that green check box after User.Read so I’ll have to look into that.

Thank you!

You’re saying your appregs are the exact same as my tutorial told you to set them up? 😊

Yep! I only create new client secrets because I did not have it previously.

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

Awesome!

My status is not the same as yours:

image

from fastapi-azure-auth.

u-iandono avatar u-iandono commented on June 19, 2024

Ah yeah, I think you're correct! I tried revoking the admin consent and I got the same error as you now:

{
  "error": "invalid_grant",
  "error_description": "AADSTS65001: The user or administrator has not consented to use the application with ID 'edd67d27-ce10-4252-8f5e-2d7b7a4bbe90' named 'BE'. Send an interactive authorization request for this user and resource.\r\nTrace ID: ca86ff3c-77b2-4874-a372-affd83d46500\r\nCorrelation ID: 71ed2675-cc1b-472a-a5fa-251b63134f98\r\nTimestamp: 2022-04-04 07:29:13Z",
  "error_codes": [
    65001
  ],
  "timestamp": "2022-04-04 07:29:13Z",
  "trace_id": "ca86ff3c-77b2-4874-a372-affd83d46500",
  "correlation_id": "71ed2675-cc1b-472a-a5fa-251b63134f98",
  "suberror": "consent_required"
}

from fastapi-azure-auth.

h3rmanj avatar h3rmanj commented on June 19, 2024

Hi!

Using delegated permissions should not require admin consent (unless specified in Azure AD). I tested a setup with .NET (should be the same for fastapi, the important thing is consent and app configuration), and got it to work with only user consent.

To make this work we need some configuration in Azure AD.

The Swagger app registrations needs the following permissions:

  • user_impersonation of the API app registration
  • email, offline_access, openid and profile from Microsoft Graph1

The API app registration needs to:

  • Expose a user_impersonation scope
  • User.Read permissions from Microsoft Graph (or any other delegated permission that doesn't require admin consent)
  • Add the Swagger app registrations Client ID to the knownClientApplications array in the manifest2

In Swagger, you have to configure the authentication request to get the .default scope of your API app registration (api://{CLIENT_ID}/.default).2

You can now use the OBO3 flow as expected.

Footnotes

  1. Delegated permissions to Microsoft Graph, the client application is in this case the Swagger app registration

  2. Gaining consent for the middle tier application 2

  3. Middle-tier access token request

from fastapi-azure-auth.

JonasKs avatar JonasKs commented on June 19, 2024

Thank you so much, both of you!

I've added documentation here.

from fastapi-azure-auth.

u-iandono avatar u-iandono commented on June 19, 2024

Thank you so much @JonasKs !

from fastapi-azure-auth.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.