Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Demonstrate B2C features #4

Merged
merged 10 commits into from
Nov 7, 2019
167 changes: 167 additions & 0 deletions README_B2C.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
page_type: sample
languages:
- python
- html
products:
- azure-active-directory
description: "This sample demonstrates a Python web application calling a Microsoft Graph that is secured using Azure Active Directory."
rayluo marked this conversation as resolved.
Show resolved Hide resolved
urlFragment: ms-identity-python-webapp
---
# Integrating B2C feature of Microsoft Identity Platform with a Python web application
rayluo marked this conversation as resolved.
Show resolved Hide resolved

## About this sample

> This sample was initially developed as a web app to demonstrate how to
> [Integrate Microsoft Identity Platform with a Python web application](https://github.com/Azure-Samples/ms-identity-python-webapp/blob/master/README.md).
> The same code base can also be used to demonstrate how to
> Integrate B2C feature of Microsoft Identity Platform with a Python web application.
rayluo marked this conversation as resolved.
Show resolved Hide resolved
> All you need is some different steps to register your app in your own B2C tenant,
> and then feed those different settings into the configuration file of this sample.

This sample covers the following:

* Update the application in Azure AD B2C
* Configure the sample to use the application
* Enable authentication in a web application using Azure Active Directory B2C
* Access a web API using Azure Active Directory B2C


### Overview

This sample demonstrates a Python web application that signs-in users with the Microsoft identity platform and calls the Microsoft Graph.
rayluo marked this conversation as resolved.
Show resolved Hide resolved

1. The python web application uses the Microsoft Authentication Library (MSAL) to obtain an access token from the Microsoft identity platform (formerly Azure AD v2.0):
2. The access token is used as a bearer token to authenticate the user when calling the Microsoft Graph.

![Overview](./ReadmeFiles/topology.png)


## Prerequisite
rayluo marked this conversation as resolved.
Show resolved Hide resolved

1. [Create an Azure Active Directory B2C tenant](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant)
1. [Register an application in Azure Active Directory B2C](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications).
1. [Create user flows in Azure Active Directory B2C](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows)
1. Have [Python 2.7+ or Python 3+](https://www.python.org/downloads/) installed


## Update the application

In the tutorial that you completed as part of the prerequisites, you added a web application in Azure AD B2C.
To enable communication with the sample in this tutorial, you need to add a redirect URI to the application in Azure AD B2C.

1. Sign in to the [Azure portal](https://portal.azure.com/).
1. Make sure you're using the directory that contains your Azure AD B2C tenant by selecting the **Directory + subscription** filter in the top menu and choosing the directory that contains your tenant.
1. Choose **All services** in the top-left corner of the Azure portal, and then search for and select **Azure AD B2C**.
1. Select **Applications**, and then select the *webapp1* application.
1. Under **Reply URL**, add something like `http://localhost:5000/getAToken`.
rayluo marked this conversation as resolved.
Show resolved Hide resolved

> Just remember, when setting up **Reply URL**, also give it a path,
> so that it would look something like `https//your_domain.com:5000/getAToken`.
> You could use any port or any path.
> Later we will set this sample to match what you register here.

1. Select **Save**.
1. On the properties page, record the application ID that you'll use when you configure the web application.
1. Select **Keys**, select **Generate key**, and select **Save**. Record the key that you'll use when you configure the web application.


## Configure the sample

### Step 1: Clone or download this repository

From your shell or command line:

```Shell
git clone https://github.com/Azure-Samples/ms-identity-python-webapp.git
```

or download and extract the repository .zip file.

> Given that the name of the sample is quite long, you might want to clone it in a folder close to the root of your hard drive, to avoid file name length limitations when running on Windows.


### Step 2: Install sample dependency

You will need to install dependencies using pip as follows:
rayluo marked this conversation as resolved.
Show resolved Hide resolved

```Shell
$ pip install -r requirements.txt
```

### Step 3: Configure the sample to use your Azure AD tenant

In the steps below, "ClientID" is the same as "Application ID" or "AppId".

#### Configure the pythonwebapp project

> Note: if you used the setup scripts, the changes below may have been applied for you

1. Use the `app_config_b2c.py` template to replace `app_config.py`.
1. Open the (now replaced) `app_config.py` file

* Update the value of `b2c_tenant` with the name of the Azure AD B2C tenant that you created.
For example, replace `fabrikamb2c` with `contoso`.
* Replace the value of `CLIENT_ID` with the application ID that you recorded.
* Replace the value of `CLIENT_SECRET` with the key that you recorded.
* Replace the value of `signupsignin_user_flow` with `b2c_1_signupsignin1`.
* Replace the value of `editprofile_user_flow` with `b2c_1_profileediting1`.
* Replace the value of `REDIRECT_PATH` with the path part you set up in **Reply URL**.
For example, `/getAToken`. It will be used by this sample app to form
an absolute URL which matches your full **Reply URL**.
* You do not have to configure the `ENDPOINT` and `SCOPE` right now


## Enable authentication

Run app.py from shell or command line. Note that the port needs to match what you've set up in your redirect_uri:
```Shell
$ flask run --port 5000
```

Now you would be able to visit `http://localhost:5000` and use the sign-in feature.
rayluo marked this conversation as resolved.
Show resolved Hide resolved
This is how you enable authentication in a web application using Azure Active Directory B2C.


## Access a web API

This sample itself does not act as a web API.
Here we assume you already have your web API up and running elsewhere in your B2C tenant,
with a specific endpoint, protected by a specific scope,
and your sample app is already granted permission to access that web API.

Now you can configure this sample to access that web API.

1. Open the (now replaced) `app_config.py` file
* Replace the value of `ENDPOINT` with the actual endpoint of your web API.
* Replace the value of `SCOPE` with a list of the actual scopes of your web API.
For example, write them as `["demo.read", "demo.write"]`.

Now, re-run your web app sample, and you will find a new link showed up,
and you can access the web API using Azure Active Directory B2C.


## Community Help and Support

Use [Stack Overflow](http://stackoverflow.com/questions/tagged/msal) to get support from the community.
Ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before.
Make sure that your questions or comments are tagged with [`azure-active-directory` `adal` `msal` `python`].

If you find a bug in the sample, please raise the issue on [GitHub Issues](../../issues).

To provide a recommendation, visit the following [User Voice page](https://feedback.azure.com/forums/169401-azure-active-directory).

## Contributing

If you'd like to contribute to this sample, see [CONTRIBUTING.MD](/CONTRIBUTING.md).

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [[email protected]](mailto:[email protected]) with any additional questions or comments.

## More information

For more information, see MSAL.Python's [conceptual documentation]("https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki"):


For more information about web apps scenarios on the Microsoft identity platform see [Scenario: Web app that calls web APIs](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-call-api-overview)

For more information about how OAuth 2.0 protocols work in this scenario and other scenarios, see [Authentication Scenarios for Azure AD](http://go.microsoft.com/fwlink/?LinkId=394414).
12 changes: 10 additions & 2 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ def logout():
app_config.AUTHORITY + "/oauth2/v2.0/logout" +
"?post_logout_redirect_uri=" + url_for("index", _external=True))

# This page is only used in B2C scenario
@app.route("/profile")
rayluo marked this conversation as resolved.
Show resolved Hide resolved
def profile():
app = _build_msal_app(authority=app_config.B2C_PROFILE_AUTHORITY)
return redirect(app.get_authorization_request_url([],
state=str(uuid.uuid4()),
redirect_uri=url_for("authorized", _external=True)))

@app.route("/graphcall")
def graphcall():
token = _get_token_from_cache(app_config.SCOPE)
Expand All @@ -71,9 +79,9 @@ def _save_cache(cache):
if cache.has_state_changed:
session["token_cache"] = cache.serialize()

def _build_msal_app(cache=None):
def _build_msal_app(cache=None, authority=None):
return msal.ConfidentialClientApplication(
app_config.CLIENT_ID, authority=app_config.AUTHORITY,
app_config.CLIENT_ID, authority=authority or app_config.AUTHORITY,
client_credential=app_config.CLIENT_SECRET, token_cache=cache)

def _get_token_from_cache(scope=None):
Expand Down
33 changes: 33 additions & 0 deletions app_config_b2c.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os

b2c_tenant = "fabrikamb2c"
signupsignin_user_flow = "b2c_1_signupsignin1"
rayluo marked this conversation as resolved.
Show resolved Hide resolved
editprofile_user_flow = "b2c_1_profileediting1"
authority_template = "https://{tenant}.b2clogin.com/{tenant}.onmicrosoftonline.com/{user_flow}"
rayluo marked this conversation as resolved.
Show resolved Hide resolved
rayluo marked this conversation as resolved.
Show resolved Hide resolved

CLIENT_SECRET = "Enter_the_Client_Secret_Here" # Our Quickstart uses this placeholder
# In your production app, we recommend you to use other ways to store your secret,
# such as KeyVault, or environment variable as described in Flask's documentation here
# https://flask.palletsprojects.com/en/1.1.x/config/#configuring-from-environment-variables
# CLIENT_SECRET = os.getenv("CLIENT_SECRET")
# if not CLIENT_SECRET:
# raise ValueError("Need to define CLIENT_SECRET environment variable")

AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=signupsignin_user_flow)
PROFILE_AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=editprofile_user_flow)

CLIENT_ID = "Enter_the_Application_Id_here"

REDIRECT_PATH = "/getAToken" # It will be used to form an absolute URL
# And that absolute URL must match your app's redirect_uri set in AAD

# This is the resource that you are going to access in your B2C tenant
ENDPOINT = ''

# These are the scopes that you defined for the web API
SCOPE = ["demo.read", "demo.write"]
rayluo marked this conversation as resolved.
Show resolved Hide resolved

SESSION_TYPE = "filesystem" # So token cache will be stored in server-side session

4 changes: 4 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ <h2>Welcome {{ user.get("name") }}!</h2>
<li><a href='/graphcall'>Call Microsoft Graph API</a></li>
{% endif %}

{% if config.get("B2C_PROFILE_AUTHORITY") %}
<li><a href='/profile'>Edit Profile</a></li>
rayluo marked this conversation as resolved.
Show resolved Hide resolved
{% endif %}

<li><a href="/logout">Logout</a></li>
<hr>
<footer style="text-align: right">Powered by MSAL Python {{ version }}</footer>
Expand Down