Using Auth0 to authenticate an HTTP-triggered C# class library Azure Function
The single HTML page under docs/index.html
acts like a SPA. A super-simple one.
When it first loads, it checks the hash fragment of the URL. There isn't any, so it knows the user is not logged in.
Note: Real-world SPAs will store their user tokens in local storage, and check them to see if the user is logged in. This super-simple SPA deliberately does not, so every time you refresh the page, you'll be logged out. It's a feature. ;)
Click the "Call API" button. The SPA will send a request without the Authorization
header to the Azure Function. The Function will reject the call as unauthorized (403
).
Click the "Login" button. The SPA will redirect to an Auth0-hosted login page, where you can authorize using social media accounts.
Note: There are lots of options on how to log someone in via Auth0. It doesn't have to be an Auth0-hosted login page. In particular, SPAs may choose to use Lock instead.
The Auth0 login redirects back to the SPA, appending user authentication information in the URL hash.
The SPA loads and sees the authentication information in the URL hash. The parseHash
call extracts both tokens (access_token
and id_token
) from the hash, validates the id_token
, and knows the user is logged in.
The SPA then parses the id_token
and displays the user information available to the SPA.
Click the "Call API" button. The SPA will send a request with the access_token
in the Authorization
header.
The Azure Function will authorize the user and return the details of the claims it can see in its token. The access_token
is usually minimal, containing only enough information to identify the user and make requests on behalf of them.
The server code is kicked off by a single line:
var (user, token) = await req.AuthenticateAsync(log);
This behaves as follows:
- The
Authorization: Bearer {access_token}
header is parsed and theaccess_token
is validated. - A
SecurityToken
is created by parsing theaccess_token
. This is what is returned astoken
in the code above, and can be used to authenticate actions against other APIs (e.g., creating a Google Calendar event for a user). - A
ClaimsPrincipal
is created with a singleClaimsIdentity
that contains all the claims from thataccess_token
. - The resulting
ClaimsPrincipal
(containing one identity for theaccess_token
) and theSecurityToken
(the parsedaccess_token
) are returned.
You can then examine the claims and take action accordingly. This sample Azure Function just returns the claims as JSON.
If there are any authentication errors at all, an exception is raised (and logged), and the Azure Function returns a 403
.
The SPA and Azure Function authentication flow follows https://auth0.com/docs/api-auth/grant/implicit
The SPA uses a simplified version of https://auth0.com/docs/quickstart/spa/jquery (in this simple example, there is no saving of tokens, no logout, no profile or scope handling, and no token renewal).
The Azure Functions side was a bit harder, since the Auth0 docs assume you're running on ASP.NET (with the capability to configure the OWIN authentication middleware). Since that's not true in the Azure Functions world, I had to take cues from the general Auth0 API docs. The manually validate JWT on .NET example was much more helpful. Finally, I used the Auth0 Python API quickstart to determine the correct handling of the Authorization
header.
There's a number of settings that need to be coordinated to get authorization working with your own accounts.
- Fork this GitHub project.
- Enable GitHub Pages under the project settings (as
master branch /docs folder
).- this will act as your SPA.
- Create an Azure Functions app.
- Set up deployment from your GitHub project.
- Create an Auth0 client (Single Page Web Application).
- In the
Allowed Callback URLs
setting, add your GH Pages URL (under your GitHub repository settings).- this setting tells Auth0 that after logging a user in, they are allowed to return to your SPA.
- In the
- In your Azure Functions app settings:
- Set
AUTH0_DOMAIN
to your Auth0 Domain (under your Auth0 client settings).- this setting tells the Function App authentication code which Auth0 account to use.
- Set
AUTH0_AUDIENCE
to your Azure Functions URL (in the Azure Functions overview).- this setting tells the Function App authentication code that it is the target audience for the
access_token
.
- this setting tells the Function App authentication code that it is the target audience for the
- Set
- In your Azure Functions CORS settings, add the domain portion of your GH Pages URL (under your GitHub repository settings).
- this setting tells your Function App that it should receive requests from the SPA.
- Create an Auth0 API, with
Identifier
set to your Azure Functions URL (in the Azure Functions overview).- this setting tells Auth0 that our Azure Functions App is the target audience for the
access_token
.
- this setting tells Auth0 that our Azure Functions App is the target audience for the
- Update
docs/index.html
:- Change
AUTH0_DOMAIN
to your Auth0 Domain (under your Auth0 client settings).- this setting tells the SPA which Auth0 account to use.
- Change
AUTH0_CLIENT_ID
to your Auth0 Client ID (under your Auth0 client settings).- this setting tells the SPA that it is the target audience for the
id_token
.
- this setting tells the SPA that it is the target audience for the
- Change
AUTH0_CALLBACK_URL
to your GH Pages URL (under your GitHub repository settings).- this setting is used by the SPA to tell Auth0 where to go after logging in a user.
- Change
FUNCTIONS_URL
to your Azure Functions URL (in the Azure Functions overview).- this setting tells the SPA that the target audience for the
access_token
is the Azure Functions app - it's also used as the base URL when calling the Azure Function
- this setting tells the SPA that the target audience for the
- Change