Skip to content

Commit

Permalink
Merge branch 'step-up-consent' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
rayluo committed Mar 24, 2024
2 parents 125dfb5 + 3966163 commit 659b64f
Show file tree
Hide file tree
Showing 9 changed files with 478 additions and 337 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
# See also https://endoflife.date/python
python-version: [3.7, 3.8, 3.9, "3.10", 3.11, 3.12]
python-version: [3.8, 3.9, "3.10", 3.11, 3.12]

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ This Identity library is an authentication/authorization library that:
## Scenarios supported

* [Web app that logs in users](https://identity-library.readthedocs.io/en/latest/#web-app-that-logs-in-users)
* [Web app that logs in users and calls a web API on their behalf](https://identity-library.readthedocs.io/en/latest/#web-app-that-logs-in-users-and-calls-a-web-api-on-their-behalf)
* [Web app that logs in users](https://identity-library.readthedocs.io/en/latest/)
* [Web app that logs in users and calls a web API on their behalf](https://identity-library.readthedocs.io/en/latest/)
* [In roadmap] Protected web API that only authenticated users can access
* [In roadmap] Protected web API that calls another (downstream) web API on behalf of the signed-in user

Expand Down
24 changes: 24 additions & 0 deletions docs/auth.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.. tip::

We recommend
`storing settings in environment variables. <https://12factor.net/config>`_.
The snippet above read data from environment variables.

..
This is a comment.
The table below was built via https://tableconvert.com/restructuredtext-generator
.. admonition:: Initializing Auth object differently based on Identity Provider type

+------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------------------+
| | Its authority URL looks like | Initialize Auth() object like this |
+================================================+===========================================================+==========================================================================+
| Microsoft Entra ID | ``https://login.microsoftonline.com/tenant`` | Auth(..., authority=url, ...) |
+------------------------------------------------+-----------------------------------------------------------+ +
| Microsoft Entra External ID | ``https://contoso.ciamlogin.com/contoso.onmicrosoft.com`` | |
+------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------------------+
| Microsoft Entra External ID with Custom Domain | ``https://contoso.com/tenant`` | Auth(..., oidc_authority=url, ...) |
+------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------------------+
| Azure AD B2C | N/A | Auth(..., b2c_tenant_name="contoso", b2c_signup_signin_user_flow="susi") |
+------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------------------+

106 changes: 106 additions & 0 deletions docs/django.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
Identity for Django
===================

Prerequisite
------------

Create a hello world web project in Django.

You can use
`Django's own tutorial, part 1 <https://docs.djangoproject.com/en/5.0/intro/tutorial01/>`_
as a reference. What we need are basically these steps:

1. ``django-admin startproject mysite``
2. ``python manage.py migrate``
3. ``python manage.py runserver localhost:5000``
You must use a port matching your redirect_uri that you registered.

4. Now, add an `index` view to your project.
For now, it can simply return a "hello world" page to any visitor::

from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. Everyone can read this line.")

Django configuration
--------------------

1. Firstly, create an instance of the :py:class:`identity.django.Auth` object,
and assign it to a global variable inside your ``settings.py``::

import os
from dotenv import load_dotenv
from identity.django import Auth
load_dotenv()
AUTH = Auth(
os.getenv('CLIENT_ID'),
client_credential=os.getenv('CLIENT_SECRET'),
redirect_uri=os.getenv('REDIRECT_URI'),
..., # See below on how to feed in the authority url parameter
)

.. include:: auth.rst

2. Inside the same ``settings.py`` file,
add ``"identity"`` into the ``INSTALLED_APPS`` list,
to enable the default templates came with the identity package::

INSTALLED_APPS = [
...,
"identity",
]

3. Add the built-in views into your ``urls.py``::

from django.conf import settings

urlpatterns = [
settings.AUTH.urlpattern,
...
]

Django Web App Sign In
----------------------

4. In your web project's ``views.py``, decorate some views with the
:py:func:`identity.django.Auth.login_required` decorator::

from django.conf import settings

@settings.AUTH.login_required
def index(request, *, context):
user = context['user']
return HttpResponse(f"Hello, {user.get('name')}.")

Web app that logs in users and calls a web API on their behalf
--------------------------------------------------------------

5. Decorate your token-consuming views using the same
:py:func:`identity.django.Auth.login_required` decorator,
this time with a parameter ``scopes=["your_scope_1", "your_scope_2"]``.

Then, inside your view, the token will be readily available via
``context['access_token']``. For example::

@settings.AUTH.login_required(scopes=["your_scope"])
def call_api(request, *, context):
api_result = requests.get( # Use access token to call a web api
"https://your_api.example.com",
headers={'Authorization': 'Bearer ' + context['access_token']},
timeout=30,
).json() # Here we assume the response format is json
...

All of the content above are demonstrated in
`this django web app sample <https://github.com/Azure-Samples/ms-identity-python-webapp-django>`_.


API for Django web projects
---------------------------

.. autoclass:: identity.django.Auth
:members:
:inherited-members:

.. automethod:: __init__

59 changes: 59 additions & 0 deletions docs/generic.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
Low-level API for generic web projects
======================================

Web app that logs in users
--------------------------

1. Firstly, create an instance of the :py:class:`identity.web.Auth` object,
and assign it to a (typically global) variable::

auth = identity.web.Auth(
session=session, # A session object is a key-value storage with a
# dict-like interface. Many web frameworks provide this.
authority="https://login.microsoftonline.com/common",
client_id="your_app_client_id",
client_credential="your_secret",
)

2. Now, in your web app's login controller, call the
``auth.log_in(scopes=["your_scope"], redirect_uri="https://your_app.example.com/redirect_uri")``
(see also :py:meth:`.log_in`)
to obtain the ``auth_uri`` (and possibly a ``user_code``),
and then render them into your login html page.

3. The second leg of log-in needs to be implemented in another controller,
which calls ``auth.complete_log_in(incoming_query_parameters)``
(see also :py:meth:`.complete_log_in`).
If its returned dict contains an ``error``, then render the error to end user,
otherwise your end user has successfully logged in,
and his/her information is available as a dict returned by
:meth:`identity.web.Auth.get_user`.
In particular, the returned dict contains a key named ``sub``,
whose value is the unique identifier which you can use to represent this end user
in your app's local database.

4. Don't forget to add one more controller for log out. You do it by calling
``auth.log_out("https://your_app.example.com")``.
Please refer to :meth:`.log_out`'s docs for more details about its return value.

All of the content above are demonstrated in this sample (link to be provided).


Web app that logs in users and calls a web API on their behalf
--------------------------------------------------------------

Building on top of the previous scenario, you just need to call
``auth.get_token_for_user(["your_scope"])`` to obtain a token object.
See :py:meth:`identity.web.Auth.get_token_for_user` for more details.
And you can see it in action in this sample (link to be provided).


Generic API, currently used for Flask web apps
----------------------------------------------

.. autoclass:: identity.web.Auth
:members:
:inherited-members:

.. automethod:: __init__

Loading

0 comments on commit 659b64f

Please sign in to comment.