diff --git a/articles/quickstart/backend/acul/files/auth_config.md b/articles/quickstart/backend/acul/files/auth_config.md new file mode 100644 index 0000000000..63471fea63 --- /dev/null +++ b/articles/quickstart/backend/acul/files/auth_config.md @@ -0,0 +1,12 @@ +--- +name: auth_config.json +language: json +--- + +```json +//auth0-react-samples/Sample-01/src/auth_config.json +{ + "domain": "${account.namespace}", + "clientId": "${account.clientId}" +} +``` diff --git a/articles/quickstart/backend/acul/files/index.md b/articles/quickstart/backend/acul/files/index.md new file mode 100644 index 0000000000..25b8cf231b --- /dev/null +++ b/articles/quickstart/backend/acul/files/index.md @@ -0,0 +1,47 @@ +--- +name: index.tsx +language: javascript +--- + +```javascript +import { LoginId } from '@auth0/auth0-acul-js'; +import { useState } from 'react'; + +export const LoginIdScreen = () => { + const loginManager = new LoginId(); + const [email, setEmail] = useState(''); + + return ( +
+
+ setEmail(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + /> + + + + {loginManager.transaction.alternateConnections?.map(({ name, strategy }) => ( + + ))} +
+
+ ); +}; + +export default LoginIdScreen +``` diff --git a/articles/quickstart/backend/acul/files/settings.md b/articles/quickstart/backend/acul/files/settings.md new file mode 100644 index 0000000000..3ed683eb71 --- /dev/null +++ b/articles/quickstart/backend/acul/files/settings.md @@ -0,0 +1,34 @@ +--- +name: settings.json +language: json +--- + +```json +{ + "rendering_mode": "advanced", + "context_configuration": [ + "screen.texts" + ], + "default_head_tags_disabled": false, + "head_tags": [ + { + "attributes": { + "async": true, + "defer": true, + "integrity": [ + "ASSET_SHA" + ], + "src": "http://127.0.0.1:8080/index.js" + }, + "tag": "script" + }, + { + "attributes": { + "href": "http://127.0.0.1:8080/index.css", + "rel": "stylesheet" + }, + "tag": "link" + } + ] +} +``` diff --git a/articles/quickstart/backend/acul/index.yml b/articles/quickstart/backend/acul/index.yml new file mode 100644 index 0000000000..a91e2a37d4 --- /dev/null +++ b/articles/quickstart/backend/acul/index.yml @@ -0,0 +1,45 @@ +title: Build a Login ID screen using ACUL +logo: auth0 +thirdParty: false +languages: + - JavaScript +author: + name: Auth0 Team + email: support@auth0.com + community: false +topics: + - quickstart +contentType: tutorial +useCase: quickstart +snippets: + dependencies: quickstart/acul/dependencies + setup: quickstart/acul/setup + use: quickstart/acul/use +articles: + - interactive +default_article: interactive +show_steps: true +github: + org: auth0-samples + repo: auth0-react-samples +sdk: + name: auth0-acul-js + url: https://github.com/auth0/universal-login + logo: auth0 +requirements: + - auth0-acul-js 1.0.0 +next_steps: + - path: interactive + list: + - text: Configure other identity providers + icon: 345 + href: "/identityproviders" + - text: Enable multifactor authentication + icon: 345 + href: "/multifactor-authentication" + - text: Learn about attack protection + icon: 345 + href: "/attack-protection" + - text: Learn about rules + icon: 345 + href: "/rules" \ No newline at end of file diff --git a/articles/quickstart/backend/acul/interactive.md b/articles/quickstart/backend/acul/interactive.md new file mode 100644 index 0000000000..640c9b13cd --- /dev/null +++ b/articles/quickstart/backend/acul/interactive.md @@ -0,0 +1,115 @@ +--- +title: Build a Login ID screen using ACUL +description: Learn how to build a Login screen using ACUL +interactive: true +files: + - files/settings + - files/auth_config + - files/index +github: + path: https://github.com/auth0-samples/auth0-react-samples/tree/master/Sample-01 +locale: en-US +--- + +# Build a Login ID screen using ACUL + + +

`

+ +## Configure Auth0 + + +

To use Auth0 services, you’ll need to have an application set up in the Auth0 Dashboard. The Auth0 application is where you will configure how you want authentication to work for the project you are developing.

Configure an application

Use the interactive selector to create a new Auth0 application or select an existing application that represents the project you want to integrate with. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code will use to call Auth0 APIs through the SDK.

Any settings you configure using this quickstart will automatically update for your Application in the Dashboard, which is where you can manage your Applications in the future.

If you would rather explore a complete configuration, you can view a sample application instead.

Configure Callback URLs

A callback URL is a URL in your application that you would like Auth0 to redirect users to after they have authenticated. If not set, users will not be returned to your application after they log in.

If you are following along with our sample project, set this to http://localhost:3000.

Configure Logout URLs

A logout URL is a URL in your application that you would like Auth0 to redirect users to after they have logged out. If not set, users will not be able to log out from your application and will receive an error.

If you are following along with our sample project, set this to http://localhost:3000.

Configure Allowed Web Origins

An Allowed Web Origin is a URL that you want to be allowed to access to your authentication flow. This must contain the URL of your project. If not properly set, your project will be unable to silently refresh authentication tokens, so your users will be logged out the next time they visit your application or refresh a page.

If you are following along with our sample project, set this to http://localhost:3000.

+ +## Configure ACUL for Login ID screen {{{ data-action="code" data-code="settings.json" }}} + + +

Use Auth0 CLI to enable ACLU Login ID screen in your tenant. + + + +In the root directory of your project, save the settings.json file.

Enable ACUL by running the following command in your terminal:

auth0 ul customize --rendering-mode advanced --prompt login-id --screen login-id --settings-file ./settings.json
+
+
+ +

Development Setup: This example is using localhost (127.0.0.1:8080) for development.

For production, you will need to update these URLs to point to your CDN or static hosting service.

+ +## Initiate Universal Login {{{ data-action="code" data-code="auth_config.json" }}} + + +

Use one of the sample apps provided by Auth0 to initiate Universal Login

In the root folder of your project, clone the Auth0 sample application using the following command: + + + +

git clone https://github.com/auth0-samples/auth0-react-samples
+
+
+ +

Change directory to the auth0-react-samples/Sample-01 folder and install the sample application using the following command:

cd auth0-react-samples/Sample-01
+
+npm install
+
+
+ +

Change directory to the auth0-react-samples/Sample-01/src folder and add the auth_config.json file. Edit the file to add your tenant's Custom Domain.

Run the application

npm run dev
+
+
+ +

ACUL Login ID screen Step 2 - Checkpoint
  1. Open your application (default: http://localhost:3000)

  2. Select the Log In button on the sample app

  3. You should be redirected to your Auth0 domain

After selecting Log In, you should see a blank page.

This is expected! It means Auth0 is trying to load your custom UI assets, which we have not created yet.

+ +
+ +

If you see the default Auth0 page instead of a blank page:

  1. Check if your custom domain is properly configured.

  2. Ensure your application is using the custom domain.

+ +
+ +

+ +

+ +

+ +## Build a custom interface for login-id screen {{{ data-action="code" data-code="index.tsx" }}} + + +

Run a single-page application to build custom login screens.

Configure the Boilerplate application

1. In the root folder of your project, open a new terminal and clone the Auth0 boilerplate application using the following command:

git clone https://github.com/auth0-samples/auth0-acul-react-boilerplate
+
+
+ +

2. Change directory to the auth0-acul-react-boilerplate folder and install the application and the ACUL JS SDK.

// open the directory where you git clone the boilerplate
+
+cd auth0-acul-react-boilerplate && npm i
+
+
+
+// Install the ACUL JS SDK
+
+npm install @auth0/auth0-acul-js
+
+
+ +

3. Build the application

npm run build
+
+
+ +

4. Serve the assets

npx http-server dist -p 8080
+
+
+ +

The assets are served from localhost during development.

For production, you'll need to serve these assets from a CDN or static hosting service.

ACUL Login ID screen quickstart step 4 checkpoint

After selecting Log In, you are greeted with a “Hello World” page.

+ +
+ +

Make sure to have installed the ACUL JS SDK after installing the boilerplate application.

+ +

Build the ACUL Login ID screen

Change directory to the auth0-acul-react-boilerplate/src/screens/loginId/ and edit the index.tsx file.

Rebuild the application with the following command:

npm run build
+
+
+ +

ACUL Login ID screen quickstart step 4 rebuild the app checkpoint

Select Log In.

You should now see a customized login page as shown below:

+ +
+ +
+ +

diff --git a/articles/quickstart/backend/aspnet-core-webapi/files/ApiController.md b/articles/quickstart/backend/aspnet-core-webapi/files/ApiController.md new file mode 100644 index 0000000000..9f8b83b336 --- /dev/null +++ b/articles/quickstart/backend/aspnet-core-webapi/files/ApiController.md @@ -0,0 +1,30 @@ +--- +name: ApiController.cs +language: csharp +--- + +```csharp +[Route("api")] +public class ApiController : Controller +{ + [HttpGet("private")] + [Authorize] + public IActionResult Private() + { + return Ok(new + { + Message = "Hello from a private endpoint!" + }); + } + + [HttpGet("private-scoped")] + [Authorize("read:messages")] + public IActionResult Scoped() + { + return Ok(new + { + Message = "Hello from a private-scoped endpoint!" + }); + } +} +``` diff --git a/articles/quickstart/backend/aspnet-core-webapi/files/HasScopeHandler.md b/articles/quickstart/backend/aspnet-core-webapi/files/HasScopeHandler.md new file mode 100644 index 0000000000..f67f1b2cd1 --- /dev/null +++ b/articles/quickstart/backend/aspnet-core-webapi/files/HasScopeHandler.md @@ -0,0 +1,28 @@ +--- +name: HasScopeHandler.cs +language: csharp +--- + +```csharp +public class HasScopeHandler : AuthorizationHandler +{ + protected override Task HandleRequirementAsync( + AuthorizationHandlerContext context, + HasScopeRequirement requirement + ) { + // If user does not have the scope claim, get out of here + if (!context.User.HasClaim(c => c.Type == "scope" && c.Issuer == requirement.Issuer)) + return Task.CompletedTask; + + // Split the scopes string into an array + var scopes = context.User + .FindFirst(c => c.Type == "scope" && c.Issuer == requirement.Issuer).Value.Split(' '); + + // Succeed if the scope array contains the required scope + if (scopes.Any(s => s == requirement.Scope)) + context.Succeed(requirement); + + return Task.CompletedTask; + } +} +``` diff --git a/articles/quickstart/backend/aspnet-core-webapi/files/HasScopeRequirement.md b/articles/quickstart/backend/aspnet-core-webapi/files/HasScopeRequirement.md new file mode 100644 index 0000000000..fe73104225 --- /dev/null +++ b/articles/quickstart/backend/aspnet-core-webapi/files/HasScopeRequirement.md @@ -0,0 +1,18 @@ +--- +name: HasScopeRequirement.cs +language: csharp +--- + +```csharp +public class HasScopeRequirement : IAuthorizationRequirement +{ + public string Issuer { get; } + public string Scope { get; } + + public HasScopeRequirement(string scope, string issuer) + { + Scope = scope ?? throw new ArgumentNullException(nameof(scope)); + Issuer = issuer ?? throw new ArgumentNullException(nameof(issuer)); + } +} +``` diff --git a/articles/quickstart/backend/aspnet-core-webapi/files/Program.md b/articles/quickstart/backend/aspnet-core-webapi/files/Program.md new file mode 100644 index 0000000000..7b30acec02 --- /dev/null +++ b/articles/quickstart/backend/aspnet-core-webapi/files/Program.md @@ -0,0 +1,35 @@ +--- +name: Program.cs +language: csharp +--- + +```csharp +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) +.AddJwtBearer(options => +{ + options.Authority = $"https://{builder.Configuration["Auth0:Domain"]}/"; + options.Audience = builder.Configuration["Auth0:Audience"]; + options.TokenValidationParameters = new TokenValidationParameters + { + NameClaimType = ClaimTypes.NameIdentifier + }; +}); + + builder.Services + .AddAuthorization(options => + { + options.AddPolicy( + "read:messages", + policy => policy.Requirements.Add( + new HasScopeRequirement("read:messages", domain) + ) + ); + }); + + builder.Services.AddSingleton(); + +var app = builder.Build(); +app.UseAuthentication(); +app.UseAuthorization(); +``` diff --git a/articles/quickstart/backend/aspnet-core-webapi/files/appsettings.md b/articles/quickstart/backend/aspnet-core-webapi/files/appsettings.md index ba8da8ddc4..d9419a5dd9 100644 --- a/articles/quickstart/backend/aspnet-core-webapi/files/appsettings.md +++ b/articles/quickstart/backend/aspnet-core-webapi/files/appsettings.md @@ -2,7 +2,7 @@ name: appsettings.json language: json --- - + ```json { "Auth0": { diff --git a/articles/quickstart/backend/aspnet-core-webapi/interactive.md b/articles/quickstart/backend/aspnet-core-webapi/interactive.md index 75b911693b..3c88df47b9 100644 --- a/articles/quickstart/backend/aspnet-core-webapi/interactive.md +++ b/articles/quickstart/backend/aspnet-core-webapi/interactive.md @@ -1,80 +1,53 @@ --- -title: Add Authorization to an ASP.NET Core Web API application +title: Add Authorization to Your ASP.NET Core Web API Application description: This tutorial demonstrates how to add authorization to an ASP.NET Core Web API application using the standard JWT middleware. -budicon: 500 -topics: - - quickstart - - backend - - aspnetcore - - web-api -github: - path: Quickstart/01-Authorization -contentType: tutorial -useCase: quickstart -interactive: true +interactive: true files: - - files/appsettings - - files/configure-middleware - - files/has-scope-handler - - files/has-scope-requirement - - files/api-controller + - files/appsettings + - files/Program + - files/HasScopeHandler + - files/HasScopeRequirement + - files/ApiController +github: + path: https://github.com/auth0-samples/auth0-aspnetcore-webapi-samples/tree/master/Quickstart/01-Authorization +locale: en-US --- # Add Authorization to Your ASP.NET Core Web API Application -Auth0 allows you to add authentication and access user profile information in almost any application type quickly. This guide demonstrates how to integrate Auth0 with any new or existing ASP.NET Web API application using the `Microsoft.AspNetCore.Authentication.JwtBearer` package. - -If you haven't created an API in your Auth0 dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with. -Alternatively, you can read our getting started guide, which will help you set up your first API through the Auth0 Dashboard. +

Auth0 allows you to add authentication and access user profile information in almost any application type quickly. This guide demonstrates how to integrate Auth0 with any new or existing ASP.NET Web API application using the Microsoft.AspNetCore.Authentication.JwtBearer package.

If you haven't created an API in your Auth0 dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with.

Alternatively, you can read our getting started guide, which will help you set up your first API through the Auth0 Dashboard.

Note that every API in Auth0 is configured using an API Identifier; your application code will use the API Identifier as the Audience to validate the access token.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

-Note that every API in Auth0 is configured using an API Identifier; your application code will use the API Identifier as the Audience to validate the access token. +## Define permissions - -<%= include('../../../_includes/_api_auth_intro') %> - -## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and grant write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section. The following example uses the read:messages scope.

Auth0 Dashboard> Applications > APIs > [Specific API] > Permissions tab

## Install dependencies -To allow your application to validate access tokens, add a reference to the `Microsoft.AspNetCore.Authentication.JwtBearer` Nuget package: -```text -Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -``` +

To allow your application to validate access tokens, add a reference to the Microsoft.AspNetCore.Authentication.JwtBearer NuGet package:

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
 
-## Configure the middleware {{{ data-action=code data-code="Startup.cs" }}}
+
-Set up the authentication middleware by configuring it in your application's `Program.cs` file: +

-1. Register the authentication services by making a call to the `AddAuthentication` method. Configure `JwtBearerDefaults.AuthenticationScheme` as the default scheme. - -2. Register the JWT Bearer authentication scheme by making a call to the `AddJwtBearer` method. Configure your Auth0 domain as the authority and your Auth0 API Identifier as the audience, and be sure that your Auth0 domain and API Identifier are set in your application's **appsettings.json** file. +## Configure the middleware {{{ data-action="code" data-code="Program.cs" }}} -:::note -In some cases, the access token will not have a `sub` claim; in this case, the `User.Identity.Name` will be `null`. If you want to map a different claim to `User.Identity.Name`, add it to `options.TokenValidationParameters` within the `AddJwtBearer()` call. -::: -3. Add the authentication and authorization middleware to the middleware pipeline by adding calls to the `UseAuthentication` and `UseAuthorization` methods under the `var app = builder.Build();` method. +

Set up the authentication middleware by configuring it in your application's Program.cs file:

  1. Register the authentication services by making a call to the AddAuthentication method. Configure JwtBearerDefaults.AuthenticationScheme as the default scheme.

  2. Register the JWT Bearer authentication scheme by making a call to the AddJwtBearer method. Configure your Auth0 domain as the authority and your Auth0 API Identifier as the audience, and be sure that your Auth0 domain and API Identifier are set in your application's appsettings.json file. -## Validate scopes {{{ data-action=code data-code="HasScopeHandler.cs" }}} +

    In some cases, the access token will not have a sub claim; in this case, the User.Identity.Name will be null. If you want to map a different claim to User.Identity.Name, add it to options.TokenValidationParameters within the AddJwtBearer() call.

  3. Add the authentication and authorization middleware to the middleware pipeline by adding calls to the UseAuthentication and UseAuthorization methods under the var app = builder.Build(); method.

-To ensure that an access token contains the correct scopes, use Policy-Based Authorization in the ASP.NET Core: +## Validate scopes {{{ data-action="code" data-code="HasScopeHandler.cs" }}} -1. Create a new authorization requirement called `HasScopeRequirement`, which will check whether the `scope` claim issued by your Auth0 tenant is present, and if so, will check that the claim contains the requested scope. -2. Under your `Program.cs` file's `var builder = WebApplication.CreateBuilder(args);` method, add a call to the `app.AddAuthorization` method. -3. Add policies for scopes by calling `AddPolicy` for each scope. -4. Register a singleton for the `HasScopeHandler` class. -## Protect API endpoints {{{ data-action=code data-code="ApiController.cs" }}} +

To ensure that an access token contains the correct scopes, use Policy-Based Authorization in the ASP.NET Core:

  1. Create a new authorization requirement called HasScopeRequirement, which will check whether the scope claim issued by your Auth0 tenant is present, and if so, will check that the claim contains the requested scope.

  2. Under your Program.cs file's var builder = WebApplication.CreateBuilder(args); method, add a call to the app.AddAuthorization method.

  3. Add policies for scopes by calling AddPolicy for each scope.

  4. Register a singleton for the HasScopeHandler class.

-The JWT middleware integrates with the standard ASP.NET Core Authentication and Authorization mechanisms. +## Protect API endpoints {{{ data-action="code" data-code="ApiController.cs" }}} -To secure an endpoint, add the `[Authorize]` attribute to your controller action (or the entire controller if you want to protect all of its actions). -When securing endpoints that require specific scopes, make sure that the correct scope is present in the `access_token`. To do so, add the `Authorize` attribute to the `Scoped` action and pass `read:messages` as the `policy` parameter. +

The JWT middleware integrates with the standard ASP.NET Core Authentication and Authorization mechanisms.

To secure an endpoint, add the [Authorize] attribute to your controller action (or the entire controller if you want to protect all of its actions).

When securing endpoints that require specific scopes, make sure that the correct scope is present in the access_token. To do so, add the Authorize attribute to the Scoped action and pass read:messages as the policy parameter.

## Call your API diff --git a/articles/quickstart/backend/django/files/apiexample/urls.md b/articles/quickstart/backend/django/files/apiexample/urls.md new file mode 100644 index 0000000000..dd7c571d81 --- /dev/null +++ b/articles/quickstart/backend/django/files/apiexample/urls.md @@ -0,0 +1,17 @@ +--- +name: apiexample/urls.py +language: python +--- + +```python +from django.contrib import admin +from django.urls import path +from . import views + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/public', views.public), + path('api/private', views.private), + path('api/private-scoped', views.private_scoped) +] +``` diff --git a/articles/quickstart/backend/django/files/apiexample/validator.md b/articles/quickstart/backend/django/files/apiexample/validator.md new file mode 100644 index 0000000000..16b55b40c3 --- /dev/null +++ b/articles/quickstart/backend/django/files/apiexample/validator.md @@ -0,0 +1,28 @@ +--- +name: apiexample/validator.py +language: python +--- + +```python +import json +from urllib.request import urlopen + +from authlib.oauth2.rfc7523 import JWTBearerTokenValidator +from authlib.jose.rfc7517.jwk import JsonWebKey + +class Auth0JWTBearerTokenValidator(JWTBearerTokenValidator): + def __init__(self, domain, audience): + issuer = f"https://{domain}/" + jsonurl = urlopen(f"{issuer}.well-known/jwks.json") + public_key = JsonWebKey.import_key_set( + json.loads(jsonurl.read()) + ) + super(Auth0JWTBearerTokenValidator, self).__init__( + public_key + ) + self.claims_options = { + "exp": {"essential": True}, + "aud": {"essential": True, "value": audience}, + "iss": {"essential": True, "value": issuer}, + } +``` diff --git a/articles/quickstart/backend/django/files/apiexample/views.md b/articles/quickstart/backend/django/files/apiexample/views.md new file mode 100644 index 0000000000..e9f3f9f404 --- /dev/null +++ b/articles/quickstart/backend/django/files/apiexample/views.md @@ -0,0 +1,37 @@ +--- +name: apiexample/views.py +language: python +--- + +```python +from authlib.integrations.django_oauth2 import ResourceProtector +from django.http import JsonResponse +from . import validator + +require_auth = ResourceProtector() +validator = validator.Auth0JWTBearerTokenValidator( + "${account.namespace}", + "${apiIdentifier}" +) +require_auth.register_token_validator(validator) + +def public(request): + """No access token required to access this route + """ + response = "Hello from a public endpoint! You don't need to be authenticated to see this." + return JsonResponse(dict(message=response)) + +@require_auth(None) +def private(request): + """A valid access token is required to access this route + """ + response = "Hello from a private endpoint! You need to be authenticated to see this." + return JsonResponse(dict(message=response)) + +@require_auth("read:messages") +def private_scoped(request): + """A valid access token and an appropriate scope are required to access this route + """ + response = "Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this." + return JsonResponse(dict(message=response)) +``` diff --git a/articles/quickstart/backend/django/interactive.md b/articles/quickstart/backend/django/interactive.md index 458d147557..cb6d97bc86 100644 --- a/articles/quickstart/backend/django/interactive.md +++ b/articles/quickstart/backend/django/interactive.md @@ -1,57 +1,34 @@ --- -title: Add Authorization to a Django API Application +title: Add Authorization to Your Django API Application description: This tutorial demonstrates how to add authorization to a Python API built with Django. -interactive: true +interactive: true files: - - files/validator - - files/views - - files/urls + - files/apiexample/validator + - files/apiexample/views + - files/apiexample/urls github: - - path: 01-Authorization - - branch: py-vnext + path: https://github.com/auth0-samples/auth0-django-api/tree/master/01-Authorization +locale: en-US --- - - # Add Authorization to Your Django API Application -This guide demonstrates how to integrate Auth0 with any new or existing Python API built with Django. - -If you haven't created an API in your Auth0 Dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with. - -Alternatively, you can read our getting started guide, which will help you set up your first API through the Auth0 Dashboard. -Every API in Auth0 is configured using an API Identifier that your application code will use as the Audience to validate the Access Token. - -<%= include('../../../_includes/_api_auth_intro') %> +

This guide demonstrates how to integrate Auth0 with any new or existing Python API built with Django.

If you haven't created an API in your Auth0 Dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with.

Alternatively, you can read our getting started guide, which will help you set up your first API through the Auth0 Dashboard.

Every API in Auth0 is configured using an API Identifier that your application code will use as the Audience to validate the Access Token.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> - -# Configure Django to use Auth0 - -## Install dependencies -1. Add the following dependencies to your `requirements.txt`: -```python -# /requirements.txt +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and grant write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section. The following example uses the read:messages scope.

Auth0 Dashboard> Applications > APIs > [Specific API] > Permissions tab

-Authlib~=1.0.0 -Django~=4.0.3 -python-dotenv~=0.19.2 -``` +## Configure Django to use Auth0 -2. Run `pip install -r requirements.txt` -### Create a Django application +

Install dependencies

  1. Add the following dependencies to your requirements.txt: -```shell -django-admin startproject apiexample -cd apiexample -``` +

  2. Run pip install -r requirements.txt

Create a Django application

-## Create the JWT validator {{{ data-action=code data-code="apiexample/validator.py" }}} +## Create the JWT validator {{{ data-action="code" data-code="apiexample/validator.py" }}} You're going to use a library called Authlib to create a ResourceProtector, which is a type of Django view decorator that protects your resources (API views) with a given validator. @@ -65,24 +42,284 @@ You'll then override the class's `claims_options` to make sure the token's `expi Create the file `apiexample/validator.py` using the code from the interactive panel. -## Create the API views {{{ data-action=code data-code="apiexample/views.py" }}} +## Create the API views {{{ data-action="code" data-code="apiexample/views.py" }}} + + +

Next, you'll create three API views in apiexample/views.py:

The protected routes will have a require_auth decorator, which is a ResourceProtector that uses the Auth0JWTBearerTokenValidator you created earlier.

To create the Auth0JWTBearerTokenValidator, you'll pass it to your tenant's domain and the API Identifier of the API you created earlier.

The require_auth decorator on the private_scoped route accepts an additional argument "read:messages", which checks the Access Token for the Permission (Scope) you created earlier.

+ +## Add URL mappings {{{ data-action="code" data-code="apiexample/urls.py#8:10" }}} + + +

In previous steps, you added methods to the views.py file. Next, map those methods to URLs using Django's URL dispatcher, which lets you map URL patterns to views.

Add the URL patterns to your apiexample/urls.py file.

Make a Call to Your API

To make calls to your API, you will need an access token. You can retrieve an access token for testing purposes from the Test view in your API settings.

Auth0 Dashboard> Applications > API > [Specific API] > Test tab

Provide the access token as an Authorization header in your requests.


+
+
+ + + + + + + +
curl --request get \
+
+  --url 'http:///${account.namespace}.com/api_path' \
+
+  --header 'authorization: Bearer YOUR_ACCESS_TOKEN_HERE'
var client = new RestClient("http:///${account.namespace}.com/api_path");
+
+var request = new RestRequest(Method.GET);
+
+request.AddHeader("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE");
+
+IRestResponse response = client.Execute(request);
package main
+
+
+
+import (
+
+ "fmt"
+
+ "net/http"
+
+ "io/ioutil"
+
+)
+
+
+
+func main() {
+
+
+
+ url := "http:///${account.namespace}.com/api_path"
+
+
+
+ req, _ := http.NewRequest("get", url, nil)
+
+
+
+ req.Header.Add("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE")
+
+
+
+ res, _ := http.DefaultClient.Do(req)
+
+
+
+ defer res.Body.Close()
+
+ body, _ := ioutil.ReadAll(res.Body)
+
+
+
+ fmt.Println(res)
+
+ fmt.Println(string(body))
+
+
+
+}
HttpResponse<String> response = Unirest.get("http:///${account.namespace}.com/api_path")
+
+  .header("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE")
+
+  .asString();
var axios = require("axios").default;
+
+
+
+var options = {
+
+  method: 'get',
+
+  url: 'http:///${account.namespace}.com/api_path',
+
+  headers: {authorization: 'Bearer YOUR_ACCESS_TOKEN_HERE'}
+
+};
+
+
+
+axios.request(options).then(function (response) {
+
+  console.log(response.data);
+
+}).catch(function (error) {
+
+  console.error(error);
+
+});
#import <Foundation/Foundation.h>
+
+
+
+NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN_HERE" };
+
+
+
+NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http:///${account.namespace}.com/api_path"]
+
+                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
+
+                                                   timeoutInterval:10.0];
+
+[request setHTTPMethod:@"get"];
+
+[request setAllHTTPHeaderFields:headers];
+
+
+
+NSURLSession *session = [NSURLSession sharedSession];
+
+NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
+
+                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+
+                                                if (error) {
+
+                                                    NSLog(@"%@", error);
+
+                                                } else {
+
+                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
+
+                                                    NSLog(@"%@", httpResponse);
+
+                                                }
+
+                                            }];
+
+[dataTask resume];
$curl = curl_init();
+
+
+
+curl_setopt_array($curl, [
+
+  CURLOPT_URL => "http:///${account.namespace}.com/api_path",
+
+  CURLOPT_RETURNTRANSFER => true,
+
+  CURLOPT_ENCODING => "",
+
+  CURLOPT_MAXREDIRS => 10,
+
+  CURLOPT_TIMEOUT => 30,
+
+  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+
+  CURLOPT_CUSTOMREQUEST => "get",
+
+  CURLOPT_HTTPHEADER => [
+
+    "authorization: Bearer YOUR_ACCESS_TOKEN_HERE"
+
+  ],
+
+]);
+
+
+
+$response = curl_exec($curl);
+
+$err = curl_error($curl);
+
+
+
+curl_close($curl);
+
+
+
+if ($err) {
+
+  echo "cURL Error #:" . $err;
+
+} else {
+
+  echo $response;
+
+}
import http.client
+
+
+
+conn = http.client.HTTPConnection("")
+
+
+
+headers = { 'authorization': "Bearer YOUR_ACCESS_TOKEN_HERE" }
+
+
+
+conn.request("get", "/${account.namespace}.com/api_path", headers=headers)
+
+
+
+res = conn.getresponse()
+
+data = res.read()
+
+
+
+print(data.decode("utf-8"))
require 'uri'
+
+require 'net/http'
+
+
+
+url = URI("http:///${account.namespace}.com/api_path")
+
+
+
+http = Net::HTTP.new(url.host, url.port)
+
+
+
+request = Net::HTTP::Get.new(url)
+
+request["authorization"] = 'Bearer YOUR_ACCESS_TOKEN_HERE'
+
+
+
+response = http.request(request)
+
+puts response.read_body
import Foundation
+
+
+
+let headers = ["authorization": "Bearer YOUR_ACCESS_TOKEN_HERE"]
+
+
+
+let request = NSMutableURLRequest(url: NSURL(string: "http:///${account.namespace}.com/api_path")! as URL,
+
+                                        cachePolicy: .useProtocolCachePolicy,
+
+                                    timeoutInterval: 10.0)
+
+request.httpMethod = "get"
+
+request.allHTTPHeaderFields = headers
+
+
+
+let session = URLSession.shared
+
+let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
+
+  if (error != nil) {
+
+    print(error)
+
+  } else {
+
+    let httpResponse = response as? HTTPURLResponse
 
-Next, you'll create three API views in `apiexample/views.py`:
+    print(httpResponse)
 
-- `/api/public`: A public endpoint that requires no authentication.
-- `/api/private`: A private endpoint that requires a valid Access Token JWT.
-- `/api/private-scoped`: A private endpoint that requires a valid Access Token JWT containing the given `scope`.
+  }
 
-The protected routes will have a `require_auth` decorator, which is a `ResourceProtector` that uses the `Auth0JWTBearerTokenValidator` you created earlier.
+})
 
-To create the `Auth0JWTBearerTokenValidator`, you'll pass it your tenant's domain and the API Identifier of the API you created earlier.
 
-The `require_auth` decorator on the `private_scoped` route accepts an additional argument `"read:messages"`, which checks the Access Token for the Permission (Scope) you created earlier.
 
-## Add URL mappings {{{ data-action=code data-code="apiexample/urls.py#8:10" }}}
+dataTask.resume()
-In previous steps, you added methods to the `views.py` file. You need to map those methods to URLs using Django's URL dispatcher, which lets you map URL patterns to views. -Add the URL patterns to `apiexample/urls.py`: -<%= include('../_includes/_call_api') %> +

diff --git a/articles/quickstart/backend/golang/files/main.md b/articles/quickstart/backend/golang/files/main.md index 5e8d5498df..9e2ba946b6 100644 --- a/articles/quickstart/backend/golang/files/main.md +++ b/articles/quickstart/backend/golang/files/main.md @@ -2,9 +2,7 @@ name: main.go language: go --- - - - + ```go package main @@ -66,5 +64,4 @@ func main() { log.Fatalf("There was an error with the http server: %v", err) } } - ``` diff --git a/articles/quickstart/backend/golang/files/middleware/jwt.md b/articles/quickstart/backend/golang/files/middleware/jwt.md new file mode 100644 index 0000000000..0149818d66 --- /dev/null +++ b/articles/quickstart/backend/golang/files/middleware/jwt.md @@ -0,0 +1,88 @@ +--- +name: middleware/jwt.go +language: go +--- + +```go +package middleware + +import ( + "context" + "log" + "net/http" + "net/url" + "os" + "strings" + "time" + + jwtmiddleware "github.com/auth0/go-jwt-middleware/v2" + "github.com/auth0/go-jwt-middleware/v2/jwks" + "github.com/auth0/go-jwt-middleware/v2/validator" +) + +// CustomClaims contains custom data we want from the token. +type CustomClaims struct { + Scope string `json:"scope"` +} + +// Validate does nothing for this example, but we need +// it to satisfy validator.CustomClaims interface. +func (c CustomClaims) Validate(ctx context.Context) error { + return nil +} + +// EnsureValidToken is a middleware that will check the validity of our JWT. +func EnsureValidToken() func(next http.Handler) http.Handler { + issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/") + if err != nil { + log.Fatalf("Failed to parse the issuer url: %v", err) + } + + provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute) + + jwtValidator, err := validator.New( + provider.KeyFunc, + validator.RS256, + issuerURL.String(), + []string{os.Getenv("AUTH0_AUDIENCE")}, + validator.WithCustomClaims( + func() validator.CustomClaims { + return &CustomClaims{} + }, + ), + validator.WithAllowedClockSkew(time.Minute), + ) + if err != nil { + log.Fatalf("Failed to set up the jwt validator") + } + + errorHandler := func(w http.ResponseWriter, r *http.Request, err error) { + log.Printf("Encountered error while validating JWT: %v", err) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte(`{"message":"Failed to validate JWT."}`)) + } + + middleware := jwtmiddleware.New( + jwtValidator.ValidateToken, + jwtmiddleware.WithErrorHandler(errorHandler), + ) + + return func(next http.Handler) http.Handler { + return middleware.CheckJWT(next) + } +} + +// HasScope checks whether our claims have a specific scope. +func (c CustomClaims) HasScope(expectedScope string) bool { + result := strings.Split(c.Scope, " ") + for i := range result { + if result[i] == expectedScope { + return true + } + } + + return false +} +``` diff --git a/articles/quickstart/backend/golang/interactive.md b/articles/quickstart/backend/golang/interactive.md index 995103d93e..1fe58b32e5 100644 --- a/articles/quickstart/backend/golang/interactive.md +++ b/articles/quickstart/backend/golang/interactive.md @@ -1,114 +1,359 @@ --- -title: Add authorization to a Go API +title: Add Authorization to Your Go Application description: This tutorial demonstrates how to add authorization to a Go API. -topics: - - quickstart - - backend - - golang -interactive: true -github: - path: 01-Authorization-RS256 -contentType: tutorial -useCase: quickstart +interactive: true files: - - files/jwt - - files/main + - files/middleware/jwt + - files/main +github: + path: https://github.com/auth0-samples/auth0-golang-api-samples/tree/master/01-Authorization-RS256 +locale: en-US --- - - # Add Authorization to Your Go Application -This guide demonstrates how to integrate Auth0 with any new or existing Go API application using the go-jwt-middleware package. -If you have not created an API in your Auth0 dashboard yet, use the interactive selector to create a new Auth0 API or select an existing API for your project. -To set up your first API through the Auth0 dashboard, review our getting started guide. +

This guide demonstrates how to integrate Auth0 with any new or existing Go API application using the go-jwt-middleware package.

If you have not created an API in your Auth0 dashboard yet, use the interactive selector to create a new Auth0 API or select an existing API for your project.

To set up your first API through the Auth0 dashboard, review our getting started guide.

Each Auth0 API uses the API Identifier, which your application needs to validate the access token.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

-Each Auth0 API uses the API Identifier, which your application needs to validate the access token. +## Define permissions -<%= include('../../../_includes/_api_auth_intro') %> -## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and a write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section. The following example uses the read:messages scope.

Auth0 Dashboard> Applications > APIs > [Specific API] > Permissions tab

## Install dependencies -Add a `go.mod` file to list all the necessary dependencies. -```text -// go.mod +

Add a go.mod file to list all the necessary dependencies.

// go.mod
+
+
 
 module 01-Authorization-RS256
 
+
+
 go 1.21
 
+
+
 require (
+
 	github.com/auth0/go-jwt-middleware/v2 v2.2.0
+
 	github.com/joho/godotenv v1.5.1
+
 )
-```
 
-Download dependencies by running the following shell command:
+
+ +

Download dependencies by running the following shell command:

go mod download
 
-```shell
-go mod download
-```
+
+ +

## Configure your application -Create a `.env` file within the root of your project directory to store the app configuration, and fill in the -environment variables: -```sh -# The URL of our Auth0 Tenant Domain. +

Create a .env file within the root of your project directory to store the app configuration. Then, fill in the environment variables:

# The URL of our Auth0 Tenant Domain.
+
 # If you're using a Custom Domain, be sure to set this to that value instead.
+
 AUTH0_DOMAIN='${account.namespace}'
 
+
+
 # Our Auth0 API's Identifier.
+
 AUTH0_AUDIENCE='${apiIdentifier}'
-```
 
-## Create a middleware to validate access tokens {{{ data-action=code data-code="middleware/jwt.go" }}}
+
+ +

+ +## Create a middleware to validate access tokens {{{ data-action="code" data-code="middleware/jwt.go" }}} + + +

The EnsureValidToken middleware function validates the access token. You can apply this function to any endpoints you wish to protect. If the token is valid, the endpoint releases the resources. If the token is not valid, the API returns a 401 Authorization error.

Set up the go-jwt-middleware middleware to verify access tokens from incoming requests.

By default, your API will be set up to use RS256 as the algorithm for signing tokens. Since RS256 works by using a private/public keypair, tokens can be verified against the public key for your Auth0 account. This public key is accessible at https://${account.namespace}/.well-known/jwks.json.

Include a mechanism to check that the token has sufficient scope to access the requested resources.

Create a function HasScope to check and ensure the access token has the correct scope before returning a successful response.

+ +## Protect API endpoints {{{ data-action="code" data-code="main.go" }}} + + +

In this example, create an /api/public endpoint that does not use the EnsureToken middleware as it is accessible to non-authenticated requests.

Create an /api/private endpoint that requires the EnsureToken middleware as it is only available to authenticated requests containing an access token with no additional scope.

Create an /api/private-scoped endpoint that requires the EnsureToken middleware and HasScope as it is only available for authenticated requests containing an access token with the read:messages scope granted.

Only the read:messages scope is checked by the HasScope function. You may want to extend it or make it a standalone middleware that accepts multiple scopes to fit your use case.

Make a Call to Your API

To make calls to your API, you need an Access Token. You can get an Access Token for testing purposes from the Test view in your API settings.

Auth0 Dashboard> Applications > API > [Specific API] > Test tab

Provide the Access Token as an Authorization header in your requests.


+
+
+ + + + + + + +
curl --request get \
+
+  --url 'http:///${account.namespace}/api_path' \
+
+  --header 'authorization: Bearer YOUR_ACCESS_TOKEN_HERE'
var client = new RestClient("http:///${account.namespace}/api_path");
+
+var request = new RestRequest(Method.GET);
+
+request.AddHeader("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE");
+
+IRestResponse response = client.Execute(request);
package main
+
+
+
+import (
+
+ "fmt"
+
+ "net/http"
+
+ "io/ioutil"
+
+)
+
+
+
+func main() {
+
+
+
+ url := "http:///${account.namespace}/api_path"
+
+
+
+ req, _ := http.NewRequest("get", url, nil)
+
+
+
+ req.Header.Add("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE")
+
+
+
+ res, _ := http.DefaultClient.Do(req)
+
+
+
+ defer res.Body.Close()
+
+ body, _ := ioutil.ReadAll(res.Body)
+
+
+
+ fmt.Println(res)
+
+ fmt.Println(string(body))
+
+
+
+}
HttpResponse<String> response = Unirest.get("http:///${account.namespace}/api_path")
+
+  .header("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE")
+
+  .asString();
var axios = require("axios").default;
+
+
+
+var options = {
+
+  method: 'get',
+
+  url: 'http:///${account.namespace}/api_path',
+
+  headers: {authorization: 'Bearer YOUR_ACCESS_TOKEN_HERE'}
+
+};
+
+
+
+axios.request(options).then(function (response) {
+
+  console.log(response.data);
+
+}).catch(function (error) {
+
+  console.error(error);
+
+});
#import <Foundation/Foundation.h>
+
+
+
+NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN_HERE" };
+
+
+
+NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http:///${account.namespace}/api_path"]
+
+                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
+
+                                                   timeoutInterval:10.0];
+
+[request setHTTPMethod:@"get"];
+
+[request setAllHTTPHeaderFields:headers];
+
+
+
+NSURLSession *session = [NSURLSession sharedSession];
+
+NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
+
+                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+
+                                                if (error) {
+
+                                                    NSLog(@"%@", error);
+
+                                                } else {
+
+                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
+
+                                                    NSLog(@"%@", httpResponse);
+
+                                                }
+
+                                            }];
+
+[dataTask resume];
$curl = curl_init();
+
+
+
+curl_setopt_array($curl, [
+
+  CURLOPT_URL => "http:///${account.namespace}/api_path",
+
+  CURLOPT_RETURNTRANSFER => true,
+
+  CURLOPT_ENCODING => "",
+
+  CURLOPT_MAXREDIRS => 10,
+
+  CURLOPT_TIMEOUT => 30,
+
+  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+
+  CURLOPT_CUSTOMREQUEST => "get",
+
+  CURLOPT_HTTPHEADER => [
+
+    "authorization: Bearer YOUR_ACCESS_TOKEN_HERE"
+
+  ],
+
+]);
+
+
+
+$response = curl_exec($curl);
+
+$err = curl_error($curl);
+
+
+
+curl_close($curl);
+
+
+
+if ($err) {
+
+  echo "cURL Error #:" . $err;
+
+} else {
+
+  echo $response;
+
+}
import http.client
+
+
+
+conn = http.client.HTTPConnection("")
+
+
+
+headers = { 'authorization': "Bearer YOUR_ACCESS_TOKEN_HERE" }
+
+
+
+conn.request("get", "/${account.namespace}/api_path", headers=headers)
+
+
+
+res = conn.getresponse()
+
+data = res.read()
+
+
+
+print(data.decode("utf-8"))
require 'uri'
+
+require 'net/http'
+
+
+
+url = URI("http:///${account.namespace}/api_path")
+
+
+
+http = Net::HTTP.new(url.host, url.port)
+
+
+
+request = Net::HTTP::Get.new(url)
+
+request["authorization"] = 'Bearer YOUR_ACCESS_TOKEN_HERE'
+
+
+
+response = http.request(request)
+
+puts response.read_body
import Foundation
+
+
+
+let headers = ["authorization": "Bearer YOUR_ACCESS_TOKEN_HERE"]
+
+
+
+let request = NSMutableURLRequest(url: NSURL(string: "http:///${account.namespace}/api_path")! as URL,
+
+                                        cachePolicy: .useProtocolCachePolicy,
+
+                                    timeoutInterval: 10.0)
+
+request.httpMethod = "get"
+
+request.allHTTPHeaderFields = headers
+
+
+
+let session = URLSession.shared
+
+let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
+
+  if (error != nil) {
 
-The `EnsureValidToken` middleware function validates the access token. You can apply this function to any endpoints you wish to protect.
-If the token is valid, the endpoint releases the resources. If the token is not valid, the API returns a `401 Authorization` error.
+    print(error)
 
-Setup the **go-jwt-middleware** middleware to verify access tokens from incoming requests.
+  } else {
 
-<%= include('../_includes/_api_jwks_description_no_link') %>
+    let httpResponse = response as? HTTPURLResponse
 
-Include a mechanism to check that the token has sufficient **scope** to access the requested resources.
+    print(httpResponse)
 
+  }
 
-Create a function `HasScope` to check and ensure the access token has the correct scope before returning a successful response.
+})
 
-## Protect API endpoints {{{ data-action=code data-code="main.go" }}}
 
-In this example, create an `/api/public` endpoint that does not use the `EnsureToken` middleware as it is accessible to non-authenticated requests.
 
-Create an `/api/private` endpoint that requires the `EnsureToken` middleware as it is only available to authenticated requests containing an access token with no additional scope.
+dataTask.resume()
-Create an `/api/private-scoped` endpoint that requires the `EnsureToken` middleware and `HasScope` as it is only available for authenticated requests containing an access token with the `read:messages` scope granted. -::: note -Only the `read:messages` scope is checked by the `HasScope` function, you may want to extend it or make it a standalone middleware that accepts multiple scopes to fit your use case. -::: -<%= include('../_includes/_call_api') %> +

Go API Quickstart - Step 5 Checkpoint

Now that you have configured your application, run your application and verify that:

  • GET /api/public is available for non-authenticated requests.

  • GET /api/private is available for authenticated requests.

  • GET /api/private-scoped is available for authenticated requests containing an access token with the read:messages scope.

-::::checkpoint -:::checkpoint-default -Now that you have configured your application, run your application to verify that: -* `GET /api/public` is available for non-authenticated requests. -* `GET /api/private` is available for authenticated requests. -* `GET /api/private-scoped` is available for authenticated requests containing an access token with the `read:messages` scope. -::: +
-:::checkpoint-failure -If your application did not start successfully: -* Verify you added the token as the `Authorization` header -* Ensure the token has the correct scopes. Verify with jwt.io. +

If your application did not start successfully:

  • Verify you added the token as the Authorization header

  • Ensure the token has the correct scopes. Verify with jwt.io.

Still having issues? Check out our documentation or visit our community page to get more help.

-Still having issues? Check out our documentation or visit our community page to get more help. -::: -:::: +

diff --git a/articles/quickstart/backend/java-spring-security5/files/APIController.md b/articles/quickstart/backend/java-spring-security5/files/APIController.md new file mode 100644 index 0000000000..6f04ff5496 --- /dev/null +++ b/articles/quickstart/backend/java-spring-security5/files/APIController.md @@ -0,0 +1,35 @@ +--- +name: APIController.java +language: powershell +--- + +```powershell + import com.auth0.example.model.Message; + import org.springframework.http.MediaType; + import org.springframework.web.bind.annotation.CrossOrigin; + import org.springframework.web.bind.annotation.GetMapping; + import org.springframework.web.bind.annotation.RequestMapping; + import org.springframework.web.bind.annotation.RestController; + + @RestController + @RequestMapping(path = "api", produces = MediaType.APPLICATION_JSON_VALUE) + // For simplicity of this sample, allow all origins. Real applications should configure CORS for their use case. + @CrossOrigin(origins = "*") + public class APIController { + +@GetMapping(value = "/public") +public Message publicEndpoint() { + return new Message("All good. You DO NOT need to be authenticated to call /api/public."); +} + +@GetMapping(value = "/private") +public Message privateEndpoint() { + return new Message("All good. You can see this because you are Authenticated."); +} + +@GetMapping(value = "/private-scoped") +public Message privateScopedEndpoint() { + return new Message("All good. You can see this because you are Authenticated with a Token granted the 'read:messages' scope"); +} + } +``` diff --git a/articles/quickstart/backend/java-spring-security5/files/SecurityConfig.md b/articles/quickstart/backend/java-spring-security5/files/SecurityConfig.md new file mode 100644 index 0000000000..5c6788d8a4 --- /dev/null +++ b/articles/quickstart/backend/java-spring-security5/files/SecurityConfig.md @@ -0,0 +1,41 @@ +--- +name: SecurityConfig.java +language: powershell +--- + +```powershell + package com.auth0.example.security; + + import org.springframework.context.annotation.Bean; + import org.springframework.context.annotation.Configuration; + import org.springframework.security.config.annotation.web.builders.HttpSecurity; + import org.springframework.security.web.SecurityFilterChain; + + import static org.springframework.security.config.Customizer.withDefaults; + + /** + * Configures our application with Spring Security to restrict access to our API endpoints. + */ + @Configuration + public class SecurityConfig { + +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + /* + This is where we configure the security required for our endpoints and setup our app to serve as + an OAuth2 Resource Server, using JWT validation. + */ + return http + .authorizeHttpRequests((authorize) -> authorize + .requestMatchers("/api/public").permitAll() + .requestMatchers("/api/private").authenticated() + .requestMatchers("/api/private-scoped").hasAuthority("SCOPE_read:messages") + ) + .cors(withDefaults()) + .oauth2ResourceServer(oauth2 -> oauth2 + .jwt(withDefaults()) + ) + .build(); +} + } +``` diff --git a/articles/quickstart/backend/java-spring-security5/files/application.md b/articles/quickstart/backend/java-spring-security5/files/application.md index f12a521ce0..b94691c69a 100644 --- a/articles/quickstart/backend/java-spring-security5/files/application.md +++ b/articles/quickstart/backend/java-spring-security5/files/application.md @@ -1,12 +1,13 @@ --- name: application.yml -language: yaml +language: --- -```yaml + +``` okta: oauth2: # Replace with the domain of your Auth0 tenant. issuer: https://${account.namespace}/ # Replace with the API Identifier for your Auth0 API. audience: ${apiIdentifier} -``` \ No newline at end of file +``` diff --git a/articles/quickstart/backend/java-spring-security5/files/message.md b/articles/quickstart/backend/java-spring-security5/files/message.md index 99f4056914..fc1823b480 100644 --- a/articles/quickstart/backend/java-spring-security5/files/message.md +++ b/articles/quickstart/backend/java-spring-security5/files/message.md @@ -1,11 +1,11 @@ --- name: Message.java -language: java +language: powershell --- -```java -/** - * Simple domain object for our API to return a message. - */ -public record Message(String message) {} - -``` \ No newline at end of file + +```powershell + /** + * Simple domain object for our API to return a message. + */ + public record Message(String message) {} +``` diff --git a/articles/quickstart/backend/java-spring-security5/interactive.md b/articles/quickstart/backend/java-spring-security5/interactive.md index ae88837d54..c68d71b11b 100644 --- a/articles/quickstart/backend/java-spring-security5/interactive.md +++ b/articles/quickstart/backend/java-spring-security5/interactive.md @@ -1,156 +1,128 @@ --- -title: Spring Boot API -description: Secure your API using the Okta Spring Boot Starter -interactive: true -alias: - - spring security - - spring -topics: - - quickstart - - backend - - spring +title: Add Authorization to Your Spring Boot Application +description: This guide demonstrates how to integrate Auth0 with any new or existing Spring Boot application. +interactive: true files: - - files/application - - files/security-config - - files/message - - files/api-controller + - files/application + - files/SecurityConfig + - files/Message + - files/APIController github: - path: 01-Authorization-MVC + path: https://github.com/auth0-samples/auth0-spring-security5-api-sample/tree/master/01-Authorization-MVC +locale: en-US --- # Add Authorization to Your Spring Boot Application -Auth0 allows you to quickly add authorization to your application. This guide demonstrates how to integrate Auth0 with any new or existing Spring Boot application. -If you have not created an API in your Auth0 dashboard yet, use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with. +

Auth0 allows you to quickly add authorization to your application. This guide demonstrates how to integrate Auth0 with any new or existing Spring Boot application.

If you have not created an API in your Auth0 dashboard yet, use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with.

Review our getting started guide to set up your first API through the Auth0 dashboard.

Each Auth0 API uses the API Identifier, which your application needs to validate the access token.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

-Review our getting started guide to set up your first API through the Auth0 dashboard. +## Define permissions -Each Auth0 API uses the API Identifier, which your application needs to validate the access token. -<%= include('../../../_includes/_api_auth_intro') %> +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and a write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section.

Auth0 Dashboard> Applications > APIs > [Specific API] > Permissions tab

This example uses the read:messages scope.

-## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> +## Configure the sample project {{{ data-action="code" data-code="application.yml#1:6" }}} -## Configure the sample project {{{ data-action=code data-code="application.yml#1:8" }}} -The sample project uses a `/src/main/resources/application.yml` file, which configures it to use the correct Auth0 **domain** and **API Identifier** for your API. If you download the code from this page it will be automatically configured. If you clone the example from GitHub, you will need to fill it in yourself. +

The sample project uses a /src/main/resources/application.yml file, which configures it to use the correct Auth0 domain and API Identifier for your API. If you download the code from this page it will be automatically configured. If you clone the example from GitHub, you will need to fill it in yourself.

-| Attribute | Description| -| --- | --- | -| `okta.oauth2.audience` | The unique identifier for your API. If you are following the steps in this tutorial it would be `https://quickstarts/api`. | -| `okta.oauth2.issuer` | The issuer URI of the resource server, which will be the value of the `iss` claim in the JWT issued by Auth0. Spring Security will use this property to discover the authorization server's public keys and validate the JWT signature. The value will be your Auth0 domain with an `https://` prefix and a `/` suffix (the trailing slash is important). +## Install dependencies {{{ data-action="code" data-code="application.yml#1:6" }}} -## Install dependencies {{{ data-action=code data-code="application.yml#1:8" }}} -If you are using Gradle, you can add the required dependencies using the Spring Boot Gradle Plugin and the Dependency Management Plugin to resolve dependency versions: +

If you are using Gradle, you can add the required dependencies using the Spring Boot Gradle Plugin and the Dependency Management Plugin to resolve dependency versions:

// build.gradle
 
-```groovy
-// build.gradle
 
-plugins {
-    id 'java'
-    id 'org.springframework.boot' version '3.1.5'
-    id 'io.spring.dependency-management' version '1.1.3'
-}
 
-dependencies {
-    implementation 'org.springframework.boot:spring-boot-starter-web'
-    implementation 'com.okta.spring:okta-spring-boot-starter:3.0.5'
-}
-```
+    plugins {
 
-If you are using Maven, add the Spring dependencies to your `pom.xml` file:
+        id 'java'
 
-```xml
-// pom.xml
+        id 'org.springframework.boot'
 
-
-    org.springframework.boot
-    spring-boot-starter-parent
-    3.1.5
-    
-
+        version '3.1.5'
 
-
-    
-        org.springframework.boot
-        spring-boot-starter-web
-    
-    
-        com.okta.spring
-        okta-spring-boot-starter
-        3.0.5
-    
-
-```
+        id 'io.spring.dependency-management'
 
+        version '1.1.3'
 
-## Configure the resource server {{{ data-action=code data-code="SecurityConfig.java" }}}
+    }
 
-To configure the application as a Resource Server and validate the JWTs, create a class that will provide an instance of `SecurityFilterChain`, and add the `@Configuration` annotation.
 
-### Protect API endpoints
 
-<%= include('../_includes/_api_endpoints') %>
+    dependencies {
 
-The example below shows how to secure API methods using the `HttpSecurity` object provided in the `filterChain()` method of the `SecurityConfig` class. Route matchers restrict access based on the level of authorization required.
+        implementation 'org.springframework.boot:spring-boot-starter-web'
 
-::: note
-By default, Spring Security creates a `GrantedAuthority` for each scope in the `scope` claim of the JWT. This scope enables using the `hasAuthority("SCOPE_read:messages")` method to restrict access to a valid JWT that contains the `read:messages` scope.
-:::
+        implementation 'com.okta.spring:okta-spring-boot-starter:3.0.5'
 
-## Create the Domain Object {{{ data-action=code data-code="Message.java#1:11" }}}
+    }
 
-To make your endpoint return a JSON, you can use a Java record. The member variables of this object is serialized into the key value for your JSON. Create a new record named `Message` as an example domain object to return during the API calls.
+
-## Create the API controller {{{ data-action=code data-code="APIController.java" }}} +

If you are using Maven, add the Spring dependencies to your pom.xml file:

// pom.xml
 
-Create a new class named `APIController` to handle requests to the endpoints. The `APIController` has three routes as defined in the [Protect API Endpoints](/quickstart/backend/java-spring-security5/interactive/#configure-the-resource-server) section. For this example, allow all origins through `@CrossOrigin` annotation. Real applications should configure `CORS` for their use case.
 
-## Run the application {{{ data-action=code data-code="APIController.java" }}}
 
-To build and run the sample project, execute the `bootRun` Gradle task.
+<parent>
+
+ <groupId>org.springframework.boot</groupId>
+
+ <artifactId>spring-boot-starter-parent</artifactId>
+
+ <version>3.1.5</version>
+
+ <relativePath/>
+
+</parent>
+
+<dependencies>
+
+ <dependency>
+
+ <groupId>org.springframework.boot</groupId>
 
-Linux or macOS:
+ <artifactId>spring-boot-starter-web</artifactId>
 
-```bash
-./gradlew bootRun
-```
+ </dependency>
 
-Windows:
+ <dependency>
 
-```bash
-gradlew.bat bootRun
-```
+ <groupId>com.okta.spring</groupId>
 
-If you are configuring your own application using Maven and the Spring Boot Maven Plugin, you can execute the `spring-boot:run` goal.
+ <artifactId>okta-spring-boot-starter</artifactId>
 
-Linux or macOS:
+ <version>3.0.5</version>
 
-```bash
-mvn spring-boot:run
-```
+ </dependency>
 
-Windows:
+</dependencies>
 
-```bash
-mvn.cmd spring-boot:run
-```
+
+ +

+ +## Configure the resource server {{{ data-action="code" data-code="SecurityConfig.java" }}} + + +

To configure the application as a Resource Server and validate the JWTs, create a class that will provide an instance of SecurityFilterChain, and add the @Configuration annotation.

Protect API endpoints

The routes shown below are available for the following requests:

The example below shows how to secure API methods using the HttpSecurity object provided in the filterChain() method of the SecurityConfig class. Route matchers restrict access based on the level of authorization required.

By default, Spring Security creates a GrantedAuthority for each scope in the scope claim of the JWT. This scope enables using the hasAuthority("SCOPE_read:messages") method to restrict access to a valid JWT that contains the read:messages scope.

+ +## Create the Domain Object {{{ data-action="code" data-code="Message.java#1:4" }}} + + +

To make your endpoint return a JSON, you can use a Java record. The member variables of this object is serialized into the key value for your JSON. Create a new record named Message as an example domain object to return during the API calls.

+ +## Create the API controller {{{ data-action="code" data-code="APIController.java" }}} + +Create a new class named `APIController` to handle requests to the endpoints. The `APIController` has three routes as defined in the [Protect API Endpoints](/quickstart/backend/java-spring-security5/interactive/#configure-the-resource-server) section. For this example, allow all origins through `@CrossOrigin` annotation. Real applications should configure `CORS` for their use case. -::::checkpoint +## Run the application {{{ data-action="code" data-code="APIController.java" }}} -:::checkpoint-default -The sample application will be available at `http://localhost:3010/`. Read about how to test and use your API in the Using Your API article. -::: +

To build and run the sample project, execute the bootRun Gradle task.

Linux or macOS:

./gradlew bootRun

Windows:

gradlew.bat bootRun

If you are configuring your own application using Maven and the Spring Boot Maven Plugin, you can execute the spring-boot:run goal.

Linux or macOS:

mvn spring-boot:run

Windows:

mvn.cmd spring-boot:run

Spring Boot API Step 7 Checkpoint

The sample application will be available at http://localhost:3010/. Read about how to test and use your API in the Using Your API article.

-:::checkpoint-failure -If your application did not launch successfully: -* Use the Troubleshooting section to check your configuration. +
-Still having issues? Check out our documentation or visit our community page to get more help. +

If your application did not launch successfully:

Still having issues? Check out our documentation or visit our community page to get more help.

-::: -:::: +

diff --git a/articles/quickstart/backend/laravel/files/routes/api.md b/articles/quickstart/backend/laravel/files/routes/api.md new file mode 100644 index 0000000000..a966bef3c4 --- /dev/null +++ b/articles/quickstart/backend/laravel/files/routes/api.md @@ -0,0 +1,58 @@ +--- +name: routes/api.php +language: php +--- + +```php +json([ + 'message' => 'Your token is valid; you are authorized.', + ]); +})->middleware('auth'); + +Route::get('/scope', function () { + return response()->json([ + 'message' => 'Your token is valid and has the `read:messages` permission; you are authorized.', + ]); +})->middleware('auth')->can('read:messages'); + +Route::get('/', function () { + if (! auth()->check()) { + return response()->json([ + 'message' => 'You did not provide a valid token.', + ]); + } + + return response()->json([ + 'message' => 'Your token is valid; you are authorized.', + 'id' => auth()->id(), + 'token' => auth()?->user()?->getAttributes(), + ]); +}); + +Route::get('/me', function () { + $user = auth()->id(); + $profile = cache()->get($user); + + if (null === $profile) { + $endpoint = Auth0::management()->users(); + $profile = $endpoint->get($user); + $profile = Auth0::json($profile); + + cache()->put($user, $profile, 120); + } + + $name = $profile['name'] ?? 'Unknown'; + $email = $profile['email'] ?? 'Unknown'; + + return response()->json([ + 'name' => $name, + 'email' => $email, + ]); +})->middleware('auth'); +``` diff --git a/articles/quickstart/backend/laravel/interactive.md b/articles/quickstart/backend/laravel/interactive.md index 5b1430a4d4..4a2fdf965a 100644 --- a/articles/quickstart/backend/laravel/interactive.md +++ b/articles/quickstart/backend/laravel/interactive.md @@ -1,263 +1,225 @@ --- -title: Add Authorization to a Laravel Application -description: Auth0's Laravel SDK allows you to quickly add token-based authorization and route access control to your Laravel application. This guide demonstrates how to integrate Auth0 with a new or existing Laravel 9 or 10 application. -topics: - - quickstart - - backend - - laravel - - authorization - - php - - laravel -contentType: tutorial -useCase: quickstart -default: true -github: - path: app -interactive: true +title: Add Authorization to Your Laravel Application +description: This guide demonstrates how to integrate Auth0 with a new (or existing) Laravel 9 or 10 application. +interactive: true files: - - files/api + - files/routes/api +github: + path: https://github.com/auth0-samples/laravel/tree/7.x/sample +locale: en-US --- # Add Authorization to Your Laravel Application -Auth0s Laravel SDK allows you to quickly add token-based authorization and route access control to your Laravel application. This guide demonstrates how to integrate Auth0 with a new (or existing) Laravel 9 or 10 application. ---- +

Auth0's Laravel SDK allows you to quickly add token-based authorization and route access control to your Laravel application. This guide demonstrates how to integrate Auth0 with a new (or existing) Laravel 9 or 10 application.


Backend applications differ from traditional web applications in that they do not handle user authentication or have a user interface. They provide an API that other applications can interact with. They accept access tokens from Authorization headers in requests to control access to routes.

Separate front-end applications are usually built to interact with these types of backends. These can be anything from single-page applications or native or mobile apps (all of which Auth0 also provides SDKs for!)

When users need to interact with your backend application, they first authenticate with Auth0 using the frontend application. The frontend application then retrieves an access token from Auth0, which it can use to make requests to your backend application on behalf of the user.

As their name implies, access tokens are designed to address matters of access control (authorization), and do not contain information about the user. Backend applications work exclusively with access tokens. You can retrieve information about the user who created the token using the Management API, which we will demonstrate later.

-**Backend applications differ from traditional web applications in that they do not handle user authentication or have a user interface. They provide an API that other applications can interact with. They accept access tokens from `Authorization` headers in requests to control access to routes.** +## Laravel Installation -Separate front-end applications are usually built to interact with these types of backends. These can be anything from single-page applications or native or mobile apps (all of which Auth0 also provides SDKs for!) -When users need to interact with your backend application, they first authenticate with Auth0 using the frontend application. The frontend application then retrieves an access token from Auth0, which it can use to make requests to your backend application on behalf of the user. +

If you do not already have a Laravel application set up, open a shell to a suitable directory for a new project and run the following command:

composer create-project --prefer-dist laravel/laravel auth0-laravel-api ^9.0
 
-As their name implies, access tokens are designed to address matters of access control (authorization), and do not contain information about the user. **Backend applications work exclusively with access tokens.** You can retrieve information about the user who created the token using the Management API, which we will demonstrate later.
+
-## Laravel Installation +

All the commands in this guide assume you are running them from the root of your Laravel project, directory so you should cd into the new project directory:

cd auth0-laravel-api
 
-**If you do not already have a Laravel application set up**, open a shell to a suitable directory for a new project and run the following command:
+
-```shell -composer create-project --prefer-dist laravel/laravel auth0-laravel-api ^9.0 -``` +

-All the commands in this guide assume you are running them from the root of your Laravel project, directory so you should `cd` into the new project directory: +## SDK Installation -```shell -cd auth0-laravel-api -``` -## SDK Installation +

Run the following command within your project directory to install the Auth0 Laravel SDK:

composer require auth0/login:^7.8 --update-with-all-dependencies
 
-Run the following command within your project directory to install the Auth0 Laravel SDK:
+
-```shell -composer require auth0/login:^7.8 --update-with-all-dependencies -``` +

Then generate an SDK configuration file for your application:

php artisan vendor:publish --tag auth0
 
-Then generate an SDK configuration file for your application:
+
-```shell -php artisan vendor:publish --tag auth0 -``` +

## SDK Configuration -Run the following command from your project directory to download the Auth0 CLI: -```shell -curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | sh -s -- -b . -``` +

Run the following command from your project directory to download the Auth0 CLI:

curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | sh -s -- -b .
+
+
+ +

Then authenticate the CLI with your Auth0 account, choosing "as a user" when prompted:

./auth0 login
+
+
+ +

Next, create a new application with Auth0:

./auth0 apps create \
+
+--name "My Laravel Backend" \
+
+--type "regular" \
+
+--auth-method "post" \
+
+--callbacks "http://localhost:8000/callback" \
+
+--logout-urls "http://localhost:8000" \
+
+--reveal-secrets \
+
+--no-input \
+
+--json > .auth0.app.json
+
+
+ +

You should also create a new API:

./auth0 apis create \
+
+--name "My Laravel Backend API" \
+
+--identifier "https://github.com/auth0/laravel-auth0" \
+
+--offline-access \
+
+--no-input \
+
+--json > .auth0.api.json
+
+
+ +

This produces two files in your project directory that configure the SDK.

As these files contain credentials it's important to treat these as sensitive. You should ensure you do not commit these to version control. If you're using Git, you should add them to your .gitignore file:

echo ".auth0.*.json" >> .gitignore
 
-Then authenticate the CLI with your Auth0 account, choosing "as a user" when prompted:
+
-```shell -./auth0 login -``` +

-Next, create a new application with Auth0: +## Access Control {{{ data-action="code" data-code="routes/api.php#6:16" }}} -```shell -./auth0 apps create \ - --name "My Laravel Backend" \ - --type "regular" \ - --auth-method "post" \ - --callbacks "http://localhost:8000/callback" \ - --logout-urls "http://localhost:8000" \ - --reveal-secrets \ - --no-input \ - --json > .auth0.app.json -``` -You should also create a new API: +

The SDK automatically registers its authorization guard with your Laravel application for use with the api middleware, which by default Laravel applies to all routes in your application's routes/api.php file.

For the SDK to work as expected without additional configuration, you should define your routes in the routes/api.php file.

You can use the Auth0 SDK's authorization guard to restrict access to your application's routes.

To reject requests that do not contain a valid access token in the Authorization header, you can use Laravel's auth middleware:

Route::get('/private', function () {
 
-```shell
-./auth0 apis create \
-  --name "My Laravel Backend API" \
-  --identifier "https://github.com/auth0/laravel-auth0" \
-  --offline-access \
-  --no-input \
-  --json > .auth0.api.json
-```
+ return response()->json([
 
-This produces two files in your project directory that configure the SDK.
+ 'message' \=> 'Your token is valid; you are authorized.',
 
-As these files contain credentials it's important to treat these as sensitive. You should ensure you do not commit these to version control. If you're using Git, you should add them to your `.gitignore` file:
+ ]);
 
-```bash
-echo ".auth0.*.json" >> .gitignore
-```
+})->middleware('auth');
 
-## Access Control {{{ data-action=code data-code="routes/api.php#6:16" }}}
+
-The SDK automatically registers its authorization guard with your Laravel application for use with the `api` middleware, which by default Laravel applies to all routes in your application's `routes/api.php` file. +

You can also require the provided token to have specific permissions by combining this with Laravel's can middleware:

Route::get('/scope', function () {
 
-::: warning
-For the SDK to work as expected without additional configuration, **you should define your routes in the `routes/api.php` file.**
-:::
+ return response()->json([
 
-You can use the Auth0 SDK's authorization guard to restrict access to your application's routes.
+ 'message' => 'Your token is valid and has the `read:messages` permission; you are authorized.',
 
-To reject requests that do not contain a valid access token in the `Authorization` header, you can use Laravel's `auth` middleware:
+ ]);
 
-```php
-Route::get('/private', function () {
-  return response()->json([
-    'message' => 'Your token is valid; you are authorized.',
-  ]);
-})->middleware('auth');
-```
+})->middleware('auth')->can('read:messages');
 
-You can also require the provided token to have specific permissions by combining this with Laravel's `can` middleware:
+
-```php -Route::get('/scope', function () { - return response()->json([ - 'message' => 'Your token is valid and has the `read:messages` permission; you are authorized.', - ]); -})->middleware('auth')->can('read:messages'); -``` +

-## Token Information {{{ data-action=code data-code="routes/api.php#18:30" }}} +## Token Information {{{ data-action="code" data-code="routes/api.php#18:30" }}} -Information about the provided access token is available through Laravel's `Auth` Facade, or the `auth()` helper function. -For example, to retrieve the user's identifier and email address: +

Information about the provided access token is available through Laravel's Auth Facade, or the auth() helper function.

For example, to retrieve the user's identifier and email address:

Route::get('/', function () {
 
-```php
-Route::get('/', function () {
-  if (! auth()->check()) {
-    return response()->json([
-      'message' => 'You did not provide a valid token.',
-    ]);
-  }
+ if (! auth()->check()) {
+
+ return response()->json([
+
+ 'message' => 'You did not provide a valid token.',
+
+ ]);
+
+ }
+
+
+
+ return response()->json([
+
+ 'message' => 'Your token is valid; you are authorized.',
+
+ 'id' => auth()->id(),
+
+ 'token' => auth()?->user()?->getAttributes(),
+
+ ]);
 
-  return response()->json([
-    'message' => 'Your token is valid; you are authorized.',
-    'id' => auth()->id(),
-    'token' => auth()?->user()?->getAttributes(),
-  ]);
 });
-```
 
-## Retrieve User Information {{{ data-action=code data-code="routes/api.php#32:51" }}}
+
-You can retrieve information about the user who created the access token from Auth0 using the Auth0 Management API. The SDK provides a convenient wrapper for this API, accessible through the SDK's `management()` method. +

-**Before making Management API calls you must enable your application to communicate with the Management API.** This can be done from the Auth0 Dashboards API page, choosing `Auth0 Management API`, and selecting the 'Machine to Machine Applications' tab. Authorize your Laravel application, and then click the down arrow to choose the scopes you wish to grant. +## Retrieve User Information {{{ data-action="code" data-code="routes/api.php#32:51" }}} + + +

You can retrieve information about the user who created the access token from Auth0 using the Auth0 Management API. The SDK provides a convenient wrapper for this API, accessible through the SDK's management() method.

Before making Management API calls you must enable your application to communicate with the Management API. This can be done from the Auth0 Dashboard's API page, choosing Auth0 Management API, and selecting the 'Machine to Machine Applications' tab. Authorize your Laravel application, and then click the down arrow to choose the scopes you wish to grant.

For the following example, you should grant the read:users scope. A list of API endpoints and the required scopes can be found in the Management API documentation.

use Auth0\Laravel\Facade\Auth0;
 
-For the following example, you should grant the `read:users` scope. A list of API endpoints and the required scopes can be found in the Management API documentation.
 
-```php
-use Auth0\Laravel\Facade\Auth0;
 
 Route::get('/me', function () {
-  $user = auth()->id();
-  $profile = cache()->get($user);
 
-  if (null === $profile) {
-    $endpoint = Auth0::management()->users();
-    $profile = $endpoint->get($user);
-    $profile = Auth0::json($profile);
+ $user = auth()->id();
 
-    cache()->put($user, $profile, 120);
-  }
+ $profile = cache()->get($user);
 
-  $name = $profile['name'] ?? 'Unknown';
-  $email = $profile['email'] ?? 'Unknown';
 
-  return response()->json([
-    'name' => $name,
-    'email' => $email,
-  ]);
-})->middleware('auth');
-```
 
-::: note
-**You should cache user information in your application for brief periods.** This reduces the number of requests your application makes to Auth0, and improves performance. You should avoid storing user information in your application for long periods as this can lead to stale data. You should also avoid storing user information beyond the user's identifier in persistent databases.
-:::
+ if (null === $profile) {
 
-## Run the Application
+ $endpoint = Auth0::management()->users();
 
-You are now ready to start your Laravel application, so it can accept requests:
+ $profile = $endpoint->get($user);
 
-```shell
-php artisan serve
-```
+ $profile = Auth0::json($profile);
 
-## Retrieve a Test Token
 
-You can learn more about retrieving access tokens here. For this quickstart, however, you can simply use an access token from your API settings "test" view.
 
-::: note
-The `/me` route we created above will not work with a test token as there is no actual user associated with it.
-:::
+ cache()->put($user, $profile, 120);
+
+ }
+
+
+
+ $name = $profile['name'] ?? 'Unknown';
+
+ $email = $profile['email'] ?? 'Unknown';
+
 
-::::checkpoint
-:::checkpoint-default
-Open a shell and try issuing requests to your application.
 
-Begin by requesting the public route:
+ return response()->json([
 
-```shell
-curl --request GET \
-  --url http://localhost:8000/api \
-  --header 'Accept: application/json'
-```
+ 'name' => $name,
 
-Next, use your access token in an `Authorization` header to request a protected route:
+ 'email' => $email,
 
-```shell
-curl --request GET \
-  --url http://localhost:8000/api/private \
-  --header 'Accept: application/json' \
-  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
-```
+ ]);
 
-Finally, try requesting the scope-protected route, which will only succeed if the access token has the `read:messages` scope granted:
+})->middleware('auth');
 
-```shell
-curl --request GET \
-  --url http://localhost:8000/api/scope \
-  --header 'Accept: application/json' \
-  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
-```
+
-::: +

You should cache user information in your application for brief periods. This reduces the number of requests your application makes to Auth0, and improves performance. You should avoid storing user information in your application for long periods as this can lead to stale data. You should also avoid storing user information beyond the user's identifier in persistent databases.

-:::checkpoint-failure -Here are a couple of things to try: +## Run the Application + + +

You are now ready to start your Laravel application, so it can accept requests:

php artisan serve
+
+
+ +

+ +## Retrieve a Test Token -- Try running `php artisan optimize:clear` to clear Laravel's cache. -- Ensure your `.auth0.app.json` and `.auth0.api.json` files are at the root of your project. -- Ensure you have enabled your Laravel application as a Machine-to-Machine application and granted it all the necessary scopes for the `Auth0 Management API` from the Auth0 Dashboard. -Encountering problems? Check the SDK's documentation or our documentation hub. You should also consider visiting the community where our team and other community members can help answer your questions. +

You can learn more about retrieving access tokens here. For this quickstart, however, you can simply use an access token from your API settings' "test" view.

The /me route we created above will not work with a test token as there is no actual user associated with it.

Laravel Quickstart Step 8 Checkpoint

Open a shell and try issuing requests to your application.

Begin by requesting the public route:

curl --request GET \ --url http://localhost:8000/api \ --header 'Accept: application/json'

Next, use your access token in an Authorization header to request a protected route:

curl --request GET \ --url http://localhost:8000/api/private \ --header 'Accept: application/json' \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Finally, try requesting the scope-protected route, which will only succeed if the access token has the read:messages scope granted:

curl --request GET \ --url http://localhost:8000/api/scope \ --header 'Accept: application/json' \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

-::: -:::: +
-### Additional Reading +

Here are a couple of things to try:

  • Try running php artisan optimize:clear to clear Laravel's cache.

  • Ensure your .auth0.app.json and .auth0.api.json files are at the root of your project.

  • Ensure you have enabled your Laravel application as a Machine-to-Machine application and granted it all the necessary scopes for the Auth0 Management API from the Auth0 Dashboard.

Encountering problems? Check the SDK's documentation or our documentation hub. You should also consider visiting the community where our team and other community members can help answer your questions.

-- User Repositories and Models extends the Auth0 Laravel SDK to use custom user models, and how to store and retrieve users from a database. -- Hooking Events covers how to listen for events raised by the Auth0 Laravel SDK, to fully customize the behavior of your integration. -- Management API support is built into the Auth0 Laravel SDK, allowing you to interact with the Management API from your Laravel application. +

Additional Reading

diff --git a/articles/quickstart/backend/nodejs/files/server.md b/articles/quickstart/backend/nodejs/files/server.md index 859872d4de..4f8b8f5734 100644 --- a/articles/quickstart/backend/nodejs/files/server.md +++ b/articles/quickstart/backend/nodejs/files/server.md @@ -2,44 +2,42 @@ name: server.js language: javascript --- - - - + ```javascript -const express = require('express'); -const app = express(); -const { auth, requiredScopes } = require('express-oauth2-jwt-bearer'); - -// Authorization middleware. When used, the Access Token must -// exist and be verified against the Auth0 JSON Web Key Set. -const checkJwt = auth({ - audience: '${apiIdentifier}', - issuerBaseURL: `https://${account.namespace}/`, -}); - -// This route doesn't need authentication -app.get('/api/public', function(req, res) { - res.json({ - message: 'Hello from a public endpoint! You don\'t need to be authenticated to see this.' - }); -}); - -// This route needs authentication -app.get('/api/private', checkJwt, function(req, res) { - res.json({ - message: 'Hello from a private endpoint! You need to be authenticated to see this.' - }); -}); - -const checkScopes = requiredScopes('read:messages'); - -app.get('/api/private-scoped', checkJwt, checkScopes, function(req, res) { - res.json({ - message: 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.' - }); -}); - -app.listen(3000, function() { - console.log('Listening on http://localhost:3000'); -}); + const express = require('express'); + const app = express(); + const { auth, requiredScopes } = require('express-oauth2-jwt-bearer'); + + // Authorization middleware. When used, the Access Token must + // exist and be verified against the Auth0 JSON Web Key Set. + const checkJwt = auth({ + audience: '${apiIdentifier}', + issuerBaseURL: 'https://${account.namespace}/', + }); + + // This route doesn't need authentication + app.get('/api/public', function(req, res) { + res.json({ +message: 'Hello from a public endpoint! You don\'t need to be authenticated to see this.' + }); + }); + + // This route needs authentication + app.get('/api/private', checkJwt, function(req, res) { + res.json({ +message: 'Hello from a private endpoint! You need to be authenticated to see this.' + }); + }); + + const checkScopes = requiredScopes('read:messages'); + + app.get('/api/private-scoped', checkJwt, checkScopes, function(req, res) { + res.json({ +message: 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.' + }); + }); + + app.listen(3000, function() { + console.log('Listening on http://localhost:3000'); + }); ``` diff --git a/articles/quickstart/backend/nodejs/interactive.md b/articles/quickstart/backend/nodejs/interactive.md index 27c8321175..d65b2d2094 100644 --- a/articles/quickstart/backend/nodejs/interactive.md +++ b/articles/quickstart/backend/nodejs/interactive.md @@ -1,76 +1,53 @@ --- -title: Add authorization to an Express.js API application -description: This tutorial demonstrates how to add authorization to an Express.js API. -topics: -- quickstart -- backend -- express -github: - path: 01-Authorization-RS256 -contentType: tutorial -useCase: quickstart -interactive: true +title: Add Authorization to Your Express.js API Application +description: This guide demonstrates how to integrate Auth0 with any new or existing Express.js API application using the express-oauth2-jwt-bearer package. +interactive: true files: -- files/server + - files/server +github: + path: https://github.com/auth0-samples/auth0-express-api-samples/tree/master/01-Authorization-RS256 +locale: en-US --- - - # Add Authorization to Your Express.js API Application -This guide demonstrates how to integrate Auth0 with any new or existing Express.js API application using the `express-oauth2-jwt-bearer` package. -If you have not created an API in your Auth0 dashboard yet, use the interactive selector to create a new Auth0 API or select an existing project API. -To set up your first API through the Auth0 dashboard, review our getting started guide. -Each Auth0 API uses the API Identifier, which your application needs to validate the access token. +

This guide demonstrates how to integrate Auth0 with any new or existing Express.js API application using the express-oauth2-jwt-bearer package.

If you have not created an API in your Auth0 dashboard yet, use the interactive selector to create a new Auth0 API or select an existing project API.

To set up your first API through the Auth0 dashboard, review our getting started guide. Each Auth0 API uses the API Identifier, which your application needs to validate the access token.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

- +## Define permissions -<%= include('../../../_includes/_api_auth_intro') %> -## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and a write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section.

This example uses the read:messages scope.

## Install dependencies -First, install the SDK with `npm`. -```bash -npm install --save express-oauth2-jwt-bearer -``` +

First, install the SDK with npm.

npm install --save express-oauth2-jwt-bearer
+
+
+ +

-## Configure the middleware {{{ data-action=code data-code="server.js#1:10" }}} +## Configure the middleware {{{ data-action="code" data-code="server.js#1:10" }}} -Configure `express-oauth2-jwt-bearer` with your Domain and API Identifier. -The `checkJwt` middleware shown to the right checks if the user's access token included in the request is valid. If the token is not valid, the user gets a 401 Authorization error when they try to access the endpoints. +

Configure express-oauth2-jwt-bearer with your Domain and API Identifier.

The checkJwt middleware shown to the right checks if the user's access token included in the request is valid. If the token is not valid, the user gets a 401 Authorization error when they try to access the endpoints.

The middleware does not check if the token has sufficient scope to access the requested resources.

-The middleware does not check if the token has sufficient scope to access the requested resources. +## Protect API endpoints {{{ data-action="code" data-code="server.js#12:32" }}} -## Protect API endpoints {{{ data-action=code data-code="server.js#12:32" }}} -To protect an individual route by requiring a valid JWT, configure the route with the `checkJwt` middleware constructed from `express-oauth2-jwt-bearer`. +

To protect an individual route by requiring a valid JWT, configure the route with the checkJwt middleware constructed from express-oauth2-jwt-bearer.

You can configure individual routes to look for a particular scope. To achieve that, set up another middleware with the requiresScope method. Provide the required scopes and apply the middleware to any routes you want to add authorization to.

Pass the checkJwt and requiredScopes middlewares to the route you want to protect.

In this configuration, only access tokens with the read:messages scope can access the endpoint.

Make a Call to Your API

To make calls to your API, you need an Access Token. You can get an Access Token for testing purposes from the Test view in your API settings.

Provide the Access Token as an Authorization header in your requests.

curl --request GET \
 
-You can configure individual routes to look for a particular scope. To achieve that, set up another middleware with the `requiresScope` method. Provide the required scopes and apply the middleware to any routes you want to add authorization to.
+  --url http://${account.namespace}/api_path \
 
-Pass the `checkJwt` and `requiredScopes` middlewares to the route you want to protect.
+  --header 'authorization: Bearer YOUR_ACCESS_TOKEN_HERE'
 
-In this configuration, only access tokens with the `read:messages` scope can access the endpoint.
+
-<%= include('../_includes/_call_api') %> +

Node JS API Step 4 Checkpoint

Now that you have configured your application, run your application to verify that:

  • GET /api/public is available for non-authenticated requests.

  • GET /api/private is available for authenticated requests.

  • GET /api/private-scoped is available for authenticated requests containing an access token with the read:messages scope.

-::::checkpoint -:::checkpoint-default -Now that you have configured your application, run your application to verify that: -* `GET /api/public` is available for non-authenticated requests. -* `GET /api/private` is available for authenticated requests. -* `GET /api/private-scoped` is available for authenticated requests containing an access token with the `read:messages` scope. -::: +
-:::checkpoint-failure -If your application did not start successfully: -* Verify you added the token as the `Authorization` header -* Ensure the token has the correct scopes. Verify with jwt.io. +

If your application did not start successfully:

  • Verify you added the token as the Authorization header

  • Ensure the token has the correct scopes. Verify with jwt.io.

Still having issues? Check out our documentation or visit our community page to get more help.

-Still having issues? Check out our documentation or visit our community page to get more help. -::: +

diff --git a/articles/quickstart/backend/php/files/index.md b/articles/quickstart/backend/php/files/index.md index 171b3b5524..5a0ab723f3 100644 --- a/articles/quickstart/backend/php/files/index.md +++ b/articles/quickstart/backend/php/files/index.md @@ -2,31 +2,31 @@ name: index.php language: php --- - + ```php -getBearerToken( - get: ['token'], - server: ['Authorization'] - ); + $token = $sdk->getBearerToken( +get: ['token'], +server: ['Authorization'] + ); - require('router.php'); + require('router.php'); ``` diff --git a/articles/quickstart/backend/php/files/router.md b/articles/quickstart/backend/php/files/router.md index 779733d4b4..73c5831f3c 100644 --- a/articles/quickstart/backend/php/files/router.md +++ b/articles/quickstart/backend/php/files/router.md @@ -2,62 +2,62 @@ name: router.php language: php --- - + ```php - 'Hello from a public endpoint! You don\'t need to be authenticated to see this.', - 'token' => $token - ]); - }); - - Route::add('/api/private', function() use ($token) { - if ($token === null) { - http_response_code(401); - exit; - } - - routeResponse([ - 'message' => 'Hello from a private endpoint! You need to be authenticated to see this.', - 'token' => $token, - ]); - }); - - Route::add('/api/private-scoped', function() use ($token) { - if ($token === null) { - http_response_code(401); - exit; - } - - if (! in_array('read:messages', $token['scopes'], true)) { - http_response_code(401); - exit; - } - - routeResponse([ - 'message' => 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.', - 'token' => $token, - ]); - }); - - // The following route is just to avoid confusion. - // We're not using an 'index route' in this app, so redirect requests to /api/public. - Route::add('/', function() { - header('Location: /api/public'); - }); - - Route::run(); + 'Hello from a public endpoint! You don\'t need to be authenticated to see this.', + 'token' => $token +]); + }); + + Route::add('/api/private', function() use ($token) { +if ($token === null) { + http_response_code(401); + exit; +} + +routeResponse([ + 'message' => 'Hello from a private endpoint! You need to be authenticated to see this.', + 'token' => $token, +]); + }); + + Route::add('/api/private-scoped', function() use ($token) { +if ($token === null) { + http_response_code(401); + exit; +} + +if (! in_array('read:messages', $token['scopes'], true)) { + http_response_code(401); + exit; +} + +routeResponse([ + 'message' => 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.', + 'token' => $token, +]); + }); + + // The following route is just to avoid confusion. + // We're not using an 'index route' in this app, so redirect requests to /api/public. + Route::add('/', function() { +header('Location: /api/public'); + }); + + Route::run(); ``` diff --git a/articles/quickstart/backend/php/interactive.md b/articles/quickstart/backend/php/interactive.md index 8a08efb278..19b0ebf8ec 100644 --- a/articles/quickstart/backend/php/interactive.md +++ b/articles/quickstart/backend/php/interactive.md @@ -1,109 +1,66 @@ --- -title: Add endpoint authorization to your PHP application -description: "Auth0 allows you to add token-based endpoint authorization to your PHP application quickly and to protect your routes. This guide demonstrates how to integrate Auth0 with any new or existing PHP application using the Auth0 PHP SDK." -interactive: true +title: Add Authorization to Your PHP Application +description: This guide demonstrates how to integrate Auth0, add token-based authorization, and protect application routes using the Auth0 PHP SDK. +interactive: true files: - - files/index - - files/router + - files/index + - files/router github: - path: app + path: https://github.com/auth0-samples/auth0-php-api-samples/tree/main/app +locale: en-US --- # Add Authorization to Your PHP Application -Auth0 allows you to add token-based endpoint authorization to almost any application type quickly. This guide demonstrates how to integrate Auth0, add token-based authorization, and protect application routes using the Auth0 PHP SDK. -To use this quickstart, you’ll need to: -- Sign up for a free Auth0 account or log in to Auth0. -- Have a working PHP project that you want to integrate with Auth0. Alternatively, you can view or download a sample application after logging in. +

Auth0 allows you to add token-based endpoint authorization to almost any application type quickly. This guide demonstrates how to integrate Auth0, add token-based authorization, and protect application routes using the Auth0 PHP SDK.

To use this quickstart, you’ll need to:

-## Configure Auth0 {{{ data-action=configure }}} +## Configure Auth0 -To use Auth0 services, you need to have an application registered in the Auth0 Dashboard. The Auth0 application is where you configure how you want authentication to work for your project. -### Configure an application +

To use Auth0 services, you need to have an application registered in the Auth0 Dashboard. The Auth0 application is where you configure how you want authentication to work for your project.

Configure an application

Use the interactive selector to create a new Auth0 application or select an existing application that represents the project you want to integrate. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code uses to call Auth0 APIs through the SDK.

Any settings you configure using this quickstart automatically updates for your application in the Dashboard, which is where you can manage your applications in the future.

If you would rather explore a complete configuration, you can view a sample application instead.

Configure an API

Similarly, you need to create a new Auth0 API or use an existing API that represents the project you're integrating from the Dashboard. Choose a unique identifier for the API and make a note of it. You need that identifier to configure your application below.

-Use the interactive selector to create a new Auth0 application or select an existing application that represents the project you want to integrate. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code uses to call Auth0 APIs through the SDK. +## Install the Auth0 PHP SDK {{{ data-action="code" data-code="index.php" }}} -Any settings you configure using this quickstart automatically updates for your application in the Dashboard, which is where you can manage your applications in the future. -If you would rather explore a complete configuration, you can view a sample application instead. +

Auth0 provides a PHP SDK (Auth0-PHP) to simplify the process of implementing Auth0 authentication and authorization in PHP apps.

The Auth0 PHP SDK requires PSR-17 and PSR-18 installed, compatible HTTP libraries for managing network requests. If you don't have libraries available, you can install reliable choices by running the following commands in your terminal:

cd <your-project-directory>
 
-### Configure an API
+    composer require symfony/http-client nyholm/psr7
 
-Similarly, you need to create a new Auth0 API or use an existing API that represents the project you're integrating from the Dashboard. Choose a unique identifier for the API and make a note of it. You need that identifier to configure your application below.
+
-## Install the Auth0 PHP SDK {{{ data-action=code data-code="index.php" }}} +

Now, install the Auth0 PHP SDK by running the following command in your terminal:

composer require auth0/auth0-php
 
-Auth0 provides a PHP SDK (Auth0-PHP) to simplify the process of implementing Auth0 authentication and authorization in PHP apps.
+
-The Auth0 PHP SDK requires PSR-17 and PSR-18 installed, compatible HTTP libraries for managing network requests. If you don't have libraries available, you can install reliable choices by running the following commands in your terminal: +

Configure the Auth0 SDK

For the SDK to function properly, you must set the following properties in the Auth0 SDK during initialization:

PHP API Step 2 Checkpoint

Your Auth0 SDK is now properly configured. Run your application to verify that:

  • The SDK is initializing correctly.

  • Your application is not throwing any errors related to Auth0.

-```bash -cd -composer require symfony/http-client nyholm/psr7 -``` +
-Now, install the Auth0 PHP SDK by running the following command in your terminal: +

Sorry about that. Here's a couple things to double check:

  • Make sure the correct application is selected.

  • Did you save after entering your URLs?

  • Make sure the domain and client ID imported correctly.

Still having issues? Check out our documentation or visit our community page to get more help.

-```bash -composer require auth0/auth0-php -``` +

-### Configure the Auth0 SDK {{{ data-action=code data-code="index.php#7:16" }}} +## Listen for the bearer tokens {{{ data-action="code" data-code="index.php#20:23" }}} -For the SDK to function properly, you must set the following properties in the Auth0 SDK during initialization: -- `strategy`: The strategy helps guide the behavior of the SDK for the use case of your app. In this case, you want to set this to the constant `Auth0\SDK\Configuration\SdkConfiguration::STRATEGY_API`. -- `domain`: The domain of your Auth0 tenant. Generally, you find this in the Auth0 Dashboard under Application's Settings in the _Domain_ field. If you are using a custom domain, set this to the value of your custom domain instead. -- `clientId`: The ID of the Auth0 Application you set up earlier in this quickstart. You can find this in the Auth0 Dashboard under your Application's Settings in the _Client ID_ field. -- `clientSecret`: The secret of the Auth0 application you created earlier in this quickstart. Client secret is in the Auth0 Dashboard under your Application's Settings in the _Client Secret_ field. -- `audience`: The identifier of the Auth0 API you registered above. This must be provided as an array. +

Next, expand your application to retrieve and process bearer tokens. Bearer tokens are access tokens provided to your API with requests from clients on a users' behalf. Access tokens approve or deny access to routes in your application. This is referred to as endpoint authorization.

The easiest way to retrieve access tokens from a request is using the PHP SDK's getBearerToken() method. This method fetches tokens from GET parameters, POST bodies, request headers, and other sources. In this case, the PHP SDK processes tokens passed from GET requests in the token parameter or from the HTTP Authorization header.

-::::checkpoint -:::checkpoint-default +## Create and configure routes {{{ data-action="code" data-code="router.php" }}} -Your Auth0 SDK is now properly configured. Run your application to verify that: -- The SDK is initializing correctly. -- Your application is not throwing any errors related to Auth0. -::: +

Now, install a routing library to help direct incoming requests to your application. This isn't a required step, but simplifies the application structure for the purposes of this quickstart.

composer require steampixel/simple-php-router
 
-:::checkpoint-failure
-Sorry about that. Here's a couple things to double check:
-* Make sure the correct application is selected.
-* Did you save after entering your URLs?
-* Make sure the domain and client ID imported correctly.
+
-Still having issues? Check out our documentation or visit our community page to get more help. +

Create a new file in your application called router.php to define the routes. Copy in the code from the interactive panel to the right under the router.php tab.

-::: -:::: +## Configure endpoint authorization {{{ data-action="code" data-code="router.php#21:31" }}} -## Listen for bearer tokens {{{ data-action=code data-code="index.php#20:23" }}} -Next, expand your application to retrieve and process bearer tokens. Bearer tokens are access tokens provided to your API with requests from clients on a users' behalf. Access tokens approve or deny access to routes in your application. This is referred to as endpoint authorization. +

Now that you have configured your Auth0 application, the Auth0 PHP SDK, and you application retrieves bearer tokens from requests, the next step is to set up endpoint authorization for your project. The getBearerToken() method you implemented above returns a Token class that includes details on the request's access.

Since the getBearerToken() method automatically validates and verifies the incoming request, your application determines the details of the access token by evaluating the method's response. When the response is null, no valid token has been provided. Otherwise, inspect the contents of the response to learn more about the request.

In the interactive panel to the right, you can see a check if the response is null or not to filter access to your /api/private route.

-The easiest way to retrieve access tokens from a request is using the PHP SDK's `getBearerToken()` method. This method fetches tokens from GET parameters, POST bodies, request headers, and other sources. In this case, the PHP SDK processes tokens passed from GET requests in the `token` parameter or from the HTTP `Authorization` header. +## Authorize with scopes {{{ data-action="code" data-code="router.php#33:48" }}} -## Create and configure routes {{{ data-action=code data-code="router.php" }}} -Now, install a routing library to help direct incoming requests to your application. This isn't a required step, but simplifies the application structure for the purposes of this quickstart. - -```bash -composer require steampixel/simple-php-router -``` - -Create a new file in your application called `router.php` to define the routes. Copy in the code from the interactive panel to the right under the router.php tab. - -## Configue endpoint authorization {{{ data-action=code data-code="router.php#21:31" }}} - -Now that you have configured your Auth0 application, the Auth0 PHP SDK, and you application retrieves bearer tokens from requests, the next step is to set up endpoint authorization for your project. The `getBearerToken()` method you implemented above returns a `Token` class that includes details on the request's access. - -Since the `getBearerToken()` method automatically validates and verifies the incoming request, your application determines the details of the access token by evaluating the method's response. When the response is null, no valid token has been provided. Otherwise, inspect the contents of the response to learn more about the request. - -In the interactive panel to the right, you can see a check if the response is null or not to filter access to your `/api/private` route. - -## Authorize with scopes {{{ data-action=code data-code="router.php#33:48" }}} - -In some cases, you may want to filter access to a specific route based on the requested scopes in an access token. As shown in the interactive panel on the right, evaluate the contents of the 'scope' property from the `getBearerToken()` method's response to check the scopes granted by the access token. +

In some cases, you may want to filter access to a specific route based on the requested scopes in an access token. As shown in the interactive panel on the right, evaluate the contents of the 'scope' property from the getBearerToken() method's response to check the scopes granted by the access token.

diff --git a/articles/quickstart/backend/python/files/server.md b/articles/quickstart/backend/python/files/server.md index 38a189d539..7d7c5c8bc9 100644 --- a/articles/quickstart/backend/python/files/server.md +++ b/articles/quickstart/backend/python/files/server.md @@ -2,59 +2,54 @@ name: server.py language: python --- - - - + ```python -"""Python Flask API Auth0 integration example -""" + """Python Flask API Auth0 integration example + """ -from os import environ as env + from os import environ as env -from dotenv import load_dotenv, find_dotenv -from flask import Flask, jsonify -from authlib.integrations.flask_oauth2 import ResourceProtector -from validator import Auth0JWTBearerTokenValidator - -require_auth = ResourceProtector() -validator = Auth0JWTBearerTokenValidator( - "${account.namespace}", - "${apiIdentifier}" -) -require_auth.register_token_validator(validator) + from dotenv import load_dotenv, find_dotenv + from flask import Flask, jsonify + from authlib.integrations.flask_oauth2 import ResourceProtector + from validator import Auth0JWTBearerTokenValidator -APP = Flask(__name__) - - -@APP.route("/api/public") -def public(): - """No access token required.""" - response = ( - "Hello from a public endpoint! You don't need to be" - " authenticated to see this." + require_auth = ResourceProtector() + validator = Auth0JWTBearerTokenValidator( +"${account.namespace}", +"${apiIdentifier}" ) - return jsonify(message=response) + require_auth.register_token_validator(validator) + APP = Flask(__name__) -@APP.route("/api/private") -@require_auth(None) -def private(): - """A valid access token is required.""" - response = ( - "Hello from a private endpoint! You need to be" - " authenticated to see this." - ) - return jsonify(message=response) - - -@APP.route("/api/private-scoped") -@require_auth("read:messages") -def private_scoped(): - """A valid access token and scope are required.""" - response = ( - "Hello from a private endpoint! You need to be" - " authenticated and have a scope of read:messages to see" - " this." - ) - return jsonify(message=response) + @APP.route("/api/public") + def public(): +"""No access token required.""" +response = ( + "Hello from a public endpoint! You don't need to be" + " authenticated to see this." +) +return jsonify(message=response) + + @APP.route("/api/private") + @require_auth(None) + def private(): +"""A valid access token is required.""" +response = ( + "Hello from a private endpoint! You need to be" + " authenticated to see this." +) +return jsonify(message=response) + + @APP.route("/api/private-scoped") + @require_auth("read:messages") + def private_scoped(): +"""A valid access token and scope are required.""" +response = ( + "Hello from a private endpoint! You need to be" + " authenticated and have a scope of read:messages to see" + " this." +) +return jsonify(message=response) ``` diff --git a/articles/quickstart/backend/python/files/validator.md b/articles/quickstart/backend/python/files/validator.md index 75d432e488..bda3edb8ca 100644 --- a/articles/quickstart/backend/python/files/validator.md +++ b/articles/quickstart/backend/python/files/validator.md @@ -2,30 +2,27 @@ name: validator.py language: python --- - - - + ```python -import json -from urllib.request import urlopen - -from authlib.oauth2.rfc7523 import JWTBearerTokenValidator -from authlib.jose.rfc7517.jwk import JsonWebKey + import json + from urllib.request import urlopen + from authlib.oauth2.rfc7523 import JWTBearerTokenValidator + from authlib.jose.rfc7517.jwk import JsonWebKey -class Auth0JWTBearerTokenValidator(JWTBearerTokenValidator): - def __init__(self, domain, audience): - issuer = f"https://{domain}/" - jsonurl = urlopen(f"{issuer}.well-known/jwks.json") - public_key = JsonWebKey.import_key_set( - json.loads(jsonurl.read()) - ) - super(Auth0JWTBearerTokenValidator, self).__init__( - public_key - ) - self.claims_options = { - "exp": {"essential": True}, - "aud": {"essential": True, "value": audience}, - "iss": {"essential": True, "value": issuer}, - } + class Auth0JWTBearerTokenValidator(JWTBearerTokenValidator): +def __init__(self, domain, audience): + issuer = f"https://{domain}/" + jsonurl = urlopen(f"{issuer}.well-known/jwks.json") + public_key = JsonWebKey.import_key_set( + json.loads(jsonurl.read()) + ) + super(Auth0JWTBearerTokenValidator, self).__init__( + public_key + ) + self.claims_options = { + "exp": {"essential": True}, + "aud": {"essential": True, "value": audience}, + "iss": {"essential": True, "value": issuer}, + } ``` diff --git a/articles/quickstart/backend/python/interactive.md b/articles/quickstart/backend/python/interactive.md index a87911688c..62cdfd2ce7 100644 --- a/articles/quickstart/backend/python/interactive.md +++ b/articles/quickstart/backend/python/interactive.md @@ -1,69 +1,54 @@ --- -title: Add Authorization to a Flask API application -description: This tutorial demonstrates how to add authorization to a Python API built with Flask. -interactive: true +title: Add Authorization to Your Flask API Application +description: This guide demonstrates how to integrate Auth0 with any new or existing Python API built with Flask. +interactive: true files: - - files/validator - - files/server + - files/validator + - files/server github: - - path: 00-Starter-Seed - - branch: pyvnext-rewrite + path: https://github.com/auth0-samples/auth0-python-api-samples/tree/master/00-Starter-Seed +locale: en-US --- - - # Add Authorization to Your Flask API Application -This guide demonstrates how to integrate Auth0 with any new or existing Python API built with Flask. -If you haven't created an API in your Auth0 dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with. +

This guide demonstrates how to integrate Auth0 with any new or existing Python API built with Flask.

If you haven't created an API in your Auth0 dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API that represents the project you want to integrate with.

Alternatively, you can read our getting started guide that helps you set up your first API through the Auth0 dashboard.

Every API in Auth0 is configured using an API Identifier that your application code will use as the Audience to validate the Access Token.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

-Alternatively, you can read our getting started guide that helps you set up your first API through the Auth0 dashboard. +## Define permissions -Every API in Auth0 is configured using an API Identifier that your application code will use as the Audience to validate the Access Token. -<%= include('../../../_includes/_api_auth_intro') %> +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and a write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section.

Auth0 Dashboard> Applications > APIs > [Specific API] > Permissions tab

This example uses the read:messages scope.

-## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> +## Install dependencies -# Configure Flask to Use Auth0 -## Install dependencies +

Add the following dependencies to your requirements.txt:

# /requirements.txt
 
-Add the following dependencies to your `requirements.txt`:
 
-```python
-# /requirements.txt
 
-flask
-Authlib
-```
+    flask
 
-## Create the JWT validator {{{ data-action=code data-code="validator.py" }}}
+    Authlib
 
-We're going to use a library called Authlib to create a ResourceProtector, which is a type of Flask decorator that protects our resources (API routes) with a given validator.
+
-The validator will validate the Access Token that we pass to the resource by checking that it has a valid signature and claims. +

-We can use AuthLib's `JWTBearerTokenValidator` validator with a few tweaks to make sure it conforms to our requirements on validating Access Tokens. +## Create the JWT validator {{{ data-action="code" data-code="validator.py" }}} -To create our `Auth0JWTBearerTokenValidator` we need to pass it our `domain` and `audience` (API Identifier). It will then get the public key required to verify the token's signature and pass it to the `JWTBearerTokenValidator` class. -We'll then override the class's `claims_options` to make sure the token's expiry, audience and issue claims are validated according to our requirements. +

We're going to use a library called Authlib to create a ResourceProtector, which is a type of Flask decorator that protects our resources (API routes) with a given validator.

The validator will validate the Access Token that we pass to the resource by checking that it has a valid signature and claims.

We can use AuthLib's JWTBearerTokenValidator validator with a few tweaks to make sure it conforms to our requirements on validating Access Tokens.

To create our Auth0JWTBearerTokenValidator we need to pass it our domain and audience (API Identifier). It will then get the public key required to verify the token's signature and pass it to the JWTBearerTokenValidator class.

We'll then override the class's claims_options to make sure the token's expiry, audience and issue claims are validated according to our requirements.

-## Create a Flask application {{{ data-action=code data-code="server.py" }}} +## Create a Flask application {{{ data-action="code" data-code="server.py" }}} -Next we'll create a Flask application with 3 API routes: -- `/api/public` A public endpoint that requires no authentication. -- `/api/private` A private endpoint that requires a valid Access Token JWT. -- `/api/private-scoped` A private endpoint that requires a valid Access Token JWT that contains the given `scope`. +

Next we'll create a Flask application with 3 API routes:

The protected routes will have a require_auth decorator which is a ResourceProtector that uses the Auth0JWTBearerTokenValidator we created earlier.

To create the Auth0JWTBearerTokenValidator we'll pass it our tenant's domain and the API Identifier of the API we created earlier.

The require_auth decorator on the private_scoped route accepts an additional argument "read:messages", which checks the Access Token for the Permission (Scope) we created earlier.

Make a Call to Your API

To make calls to your API, you need an Access Token. You can get an Access Token for testing purposes from the Test view in your API settings.

Auth0 Dashboard> Applications > API > [Specific API] > Test tab

Provide the Access Token as an Authorization header in your requests.

curl --request GET \
 
-The protected routes will have a `require_auth` decorator which is a `ResourceProtector` that uses the `Auth0JWTBearerTokenValidator` we created earlier.
+  --url http://${account.namespace}/api_path \
 
-To create the `Auth0JWTBearerTokenValidator` we'll pass it our tenant's domain and the API Identifier of the API we created earlier.
+  --header 'authorization: Bearer YOUR_ACCESS_TOKEN_HERE'
 
-The `require_auth` decorator on the `private_scoped` route accepts an additional argument `"read:messages"`, which checks the Access Token for the Permission (Scope) we created earlier.
+
-<%= include('../_includes/_call_api') %> +

diff --git a/articles/quickstart/backend/rails/files/app/controllers/application_controller.md b/articles/quickstart/backend/rails/files/app/controllers/application_controller.md new file mode 100644 index 0000000000..75520386ba --- /dev/null +++ b/articles/quickstart/backend/rails/files/app/controllers/application_controller.md @@ -0,0 +1,12 @@ +--- +name: app/controllers/application_controller.rb +language: powershell +--- + +```powershell + # frozen_string_literal: true + + class ApplicationController < ActionController::API + include Secured + end +``` diff --git a/articles/quickstart/backend/rails/files/app/controllers/concerns/secured.md b/articles/quickstart/backend/rails/files/app/controllers/concerns/secured.md new file mode 100644 index 0000000000..c6529ef19b --- /dev/null +++ b/articles/quickstart/backend/rails/files/app/controllers/concerns/secured.md @@ -0,0 +1,67 @@ +--- +name: app/controllers/concerns/secured.rb +language: powershell +--- + +```powershell + # frozen_string_literal: true + + module Secured + extend ActiveSupport::Concern + + REQUIRES_AUTHENTICATION = { message: 'Requires authentication' }.freeze + BAD_CREDENTIALS = { +message: 'Bad credentials' + }.freeze + MALFORMED_AUTHORIZATION_HEADER = { +error: 'invalid_request', +error_description: 'Authorization header value must follow this format: Bearer access-token', +message: 'Bad credentials' + }.freeze + INSUFFICIENT_PERMISSIONS = { +error: 'insufficient_permissions', +error_description: 'The access token does not contain the required permissions', +message: 'Permission denied' + }.freeze + + def authorize +token = token_from_request + +return if performed? + +validation_response = Auth0Client.validate_token(token) + +@decoded_token = validation_response.decoded_token + +return unless (error = validation_response.error) + +render json: { message: error.message }, status: error.status + end + + def validate_permissions(permissions) +raise 'validate_permissions needs to be called with a block' unless block_given? +return yield if @decoded_token.validate_permissions(permissions) + +render json: INSUFFICIENT_PERMISSIONS, status: :forbidden + end + + private + + def token_from_request +authorization_header_elements = request.headers['Authorization']&.split + +render json: REQUIRES_AUTHENTICATION, status: :unauthorized and return unless authorization_header_elements + +unless authorization_header_elements.length == 2 + render json: MALFORMED_AUTHORIZATION_HEADER, + status: :unauthorized and return +end + +scheme, token = authorization_header_elements + +render json: BAD_CREDENTIALS, status: :unauthorized and return unless scheme.downcase == 'bearer' + +token + end + end +``` diff --git a/articles/quickstart/backend/rails/files/app/controllers/private_controller.md b/articles/quickstart/backend/rails/files/app/controllers/private_controller.md new file mode 100644 index 0000000000..336740f6cd --- /dev/null +++ b/articles/quickstart/backend/rails/files/app/controllers/private_controller.md @@ -0,0 +1,13 @@ +--- +name: app/controllers/private_controller.rb +language: powershell +--- + +```powershell + # frozen_string_literal: true + class PublicController < ApplicationController + def public +render json: { message: 'All good. You don\'t need to be authenticated to call this.' } + end +end +``` diff --git a/articles/quickstart/backend/rails/files/app/controllers/public_controller.md b/articles/quickstart/backend/rails/files/app/controllers/public_controller.md new file mode 100644 index 0000000000..f8a6db5aff --- /dev/null +++ b/articles/quickstart/backend/rails/files/app/controllers/public_controller.md @@ -0,0 +1,13 @@ +--- +name: app/controllers/public_controller.rb +language: powershell +--- + +```powershell + # frozen_string_literal: true + class PublicController < ApplicationController + def public +render json: { message: 'All good. You don\'t need to be authenticated to call this.' } + end + end +``` diff --git a/articles/quickstart/backend/rails/files/app/lib/auth0_client.md b/articles/quickstart/backend/rails/files/app/lib/auth0_client.md new file mode 100644 index 0000000000..079089b4d9 --- /dev/null +++ b/articles/quickstart/backend/rails/files/app/lib/auth0_client.md @@ -0,0 +1,66 @@ +--- +name: app/lib/auth0_client.rb +language: powershell +--- + +```powershell + # frozen_string_literal: true + + require 'jwt' + require 'net/http' + + # Auth0Client class to handle JWT token validation + class Auth0Client + # Auth0 Client Objects + Error = Struct.new(:message, :status) + Response = Struct.new(:decoded_token, :error) + Token = Struct.new(:token) do +def validate_permissions(permissions) + required_permissions = Set.new permissions + scopes = token[0]['scope'] + token_permissions = scopes.present? ? Set.new(scopes.split(" ")) : Set.new + required_permissions <= token_permissions +end + end + + # Helper Functions + def self.domain_url +"https://#{Rails.configuration.auth0.domain}/" + end + + def self.decode_token(token, jwks_hash) +JWT.decode(token, nil, true, { + algorithm: 'RS256', + iss: domain_url, + verify_iss: true, + aud: Rails.configuration.auth0.audience, + verify_aud: true, + jwks: { keys: jwks_hash[:keys] } + }) + end + + def self.get_jwks +jwks_uri = URI("#{domain_url}.well-known/jwks.json") +Net::HTTP.get_response jwks_uri + end + + # Token Validation + def self.validate_token(token) +jwks_response = get_jwks + +unless jwks_response.is_a? Net::HTTPSuccess + error = Error.new(message: 'Unable to verify credentials', status: :internal_server_error) + return Response.new(nil, error) +end + +jwks_hash = JSON.parse(jwks_response.body).deep_symbolize_keys + +decoded_token = decode_token(token, jwks_hash) + +Response.new(Token.new(decoded_token), nil) + rescue JWT::VerificationError, JWT::DecodeError => e +error = Error.new('Bad credentials', :unauthorized) +Response.new(nil, error) + end + end +``` diff --git a/articles/quickstart/backend/rails/interactive.md b/articles/quickstart/backend/rails/interactive.md index 93069f4e7b..5fc5f3a668 100644 --- a/articles/quickstart/backend/rails/interactive.md +++ b/articles/quickstart/backend/rails/interactive.md @@ -1,23 +1,17 @@ --- -title: Add authorization to a Ruby on Rails API -description: This tutorial demonstrates how to add authorization to a Ruby on Rails API. -topics: - - quickstart - - backend - - rails -github: - path: 01-Authentication-RS256 -contentType: tutorial -useCase: quickstart -interactive: true +title: Add Authorization to Your Ruby on Rails API +description: This tutorial performs access token validation using the jwt Gem within a custom Auth0Client class. +interactive: true files: - - files/application_controller - - files/auth0_client - - files/secured - - files/public - - files/private + - files/app/controllers/application_controller + - files/app/lib/auth0_client + - files/app/controllers/concerns/secured + - files/app/controllers/public_controller + - files/app/controllers/private_controller +github: + path: https://github.com/auth0-samples/auth0-rubyonrails-api-samples/tree/master/01-Authentication-RS256 +locale: en-US --- - # Add Authorization to Your Ruby on Rails API This tutorial performs access token validation using the **jwt** Gem within a custom `Auth0Client` class. A Concern called `Secured` is used to authorize endpoints which require authentication through an incoming access token. @@ -31,66 +25,56 @@ Each Auth0 API uses the API Identifier, which your application needs to validate <%= include('../../../_includes/_api_auth_intro') %> ## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> + + +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and a write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section.

Auth0 Dashboard> Applications > APIs > [Specific API] > Permissions tab

This example uses the read:messages scope.

## Install dependencies -Install the **jwt** Gem. -```bash -gem 'jwt' -bundle install -``` -## Create an Auth0Client class {{{ data-action=code data-code="app/lib/auth0_client.rb" }}} +

Install the jwt Gem.

gem 'jwt'
+
+    bundle install
+
+
+ +

+ +## Create an Auth0Client class {{{ data-action="code" data-code="app/controllers/concerns/secured.rb" }}} -Create a class called `Auth0Client`. This class decodes and verifies the incoming access token taken from the `Authorization` header of the request. -The `Auth0Client` class retrieves the public key for your Auth0 tenant and then uses it to verify the signature of the access token. The `Token` struct defines a `validate_permissions` method to look for a particular `scope` in an access token by providing an array of required scopes and check if they are present in the payload of the token. +

Create a class called Auth0Client. This class decodes and verifies the incoming access token taken from the Authorization header of the request.

The Auth0Client class retrieves the public key for your Auth0 tenant and then uses it to verify the signature of the access token. The Token struct defines a validate_permissions method to look for a particular scope in an access token by providing an array of required scopes and check if they are present in the payload of the token.

-## Define a Secured concern {{{ data-action=code data-code="app/controllers/concerns/secured.rb" }}} +## Define a Secured concern {{{ data-action="code" data-code="app/controllers/concerns/secured.rb" }}} -Create a Concern called `Secured` which looks for the access token in the `Authorization` header of an incoming request. -If the token is present, the `Auth0Client.validate_token` will use the `jwt` Gem to verify the token's signature and validate the token's claims. +

Create a Concern called Secured which looks for the access token in the Authorization header of an incoming request.

If the token is present, the Auth0Client.validate_token will use the jwt Gem to verify the token's signature and validate the token's claims.

In addition to verifying that the access token is valid, the Concern also includes a mechanism for confirming the token has the sufficient scope to access the requested resources. In this example we define a validate_permissions method that receives a block and checks the permissions by calling the Token.validate_permissions method from the Auth0Client class.

For the /private-scoped route, the scopes defined will be intersected with the scopes coming in the payload, to determine if it contains one or more items from the other array.

-In addition to verifying that the access token is valid, the Concern also includes a mechanism for confirming the token has the sufficient **scope** to access the requested resources. In this example we define a `validate_permissions` method that receives a block and checks the permissions by calling the `Token.validate_permissions` method from the `Auth0Client` class. +## Include the Secure concern in your ApplicationController {{{ data-action="code" data-code="app/controllers/application_controller.rb" }}} -For the `/private-scoped` route, the scopes defined will be intersected with the scopes coming in the payload, to determine if it contains one or more items from the other array. -## Include the Secure concern in your ApplicationController {{{ data-action=code data-code="app/controllers/application_controller.rb" }}} +

By adding the Secure concern to your application controller, you'll only need to use a before_action filter in the controller that requires authorization.

-By adding the `Secure` concern to your application controller, you'll only need to use a `before_action` filter in the controller that requires authorization. +## Create the public endpoint {{{ data-action="code" data-code="app/controllers/public_controller.rb" }}} -## Create the public endpoint {{{ data-action=code data-code="app/controllers/public_controller.rb" }}} -Create a controller to handle the public endpoint `/api/public`. +

Create a controller to handle the public endpoint /api/public.

The /public endpoint does not require any authorization so no before_action is needed.

-The `/public` endpoint does not require any authorization so no `before_action` is needed. +## Create the private endpoints {{{ data-action="code" data-code="app/controllers/private_controller.rb" }}} -## Create the private endpoints {{{ data-action=code data-code="app/controllers/private_controller.rb" }}} -Create a controller to handle the private endpoints: `/api/private` and `/api/private-scoped`. +

Create a controller to handle the private endpoints: /api/private and /api/private-scoped.

/api/private is available for authenticated requests containing an access token with no additional scopes.

/api/private-scoped is available for authenticated requests containing an access token with the read:messages scope granted

The protected endpoints need to call the authorize method from the Secured concern, for that you use before_action :authorize, this ensure the Secured.authorize method is called before every action in the PrivateController.

Make a Call to Your API

To make calls to your API, you need an Access Token. You can get an Access Token for testing purposes from the Test view in your API settings.

Auth0 Dashboard> Applications > API > [Specific API] > Test tab

Provide the Access Token as an Authorization header in your requests.

curl --request GET \
 
-`/api/private` is available for authenticated requests containing an access token with no additional scopes.
+  --url http://${account.namespace}/api_path \
 
-`/api/private-scoped` is available for authenticated requests containing an access token with the `read:messages` scope granted
+  --header 'authorization: Bearer YOUR_ACCESS_TOKEN_HERE'
 
-The protected endpoints need to call the `authorize` method from the `Secured` concern, for that you use `before_action :authorize`, this ensure the `Secured.authorize` method is called before every action in the `PrivateController`. 
+
-<%= include('../_includes/_call_api') %> +

Ruby on rails Step 7 Checkpoint

Now that you have configured your application, run your application to verify that:

  • GET /api/public is available for non-authenticated requests.

  • GET /api/private is available for authenticated requests.

  • GET /api/private-scoped is available for authenticated requests containing an Access Token with the read:messages scope.

-::::checkpoint -:::checkpoint-default -Now that you have configured your application, run your application to verify that: -* `GET /api/public` is available for non-authenticated requests. -* `GET /api/private` is available for authenticated requests. -* `GET /api/private-scoped` is available for authenticated requests containing an Access Token with the `read:messages` scope. -::: +
-:::checkpoint-failure -If your application did not start successfully: -* Verify you added the token as the `Authorization` header -* Ensure the token has the correct scopes. Verify with jwt.io. +

If your application did not start successfully:

  • Verify you added the token as the Authorization header

  • Ensure the token has the correct scopes. Verify with jwt.io.

Still having issues? Check out our documentation or visit our community page to get more help.

-Still having issues? Check out our documentation or visit our community page to get more help. -::: +

diff --git a/articles/quickstart/backend/webapi-owin/files/ApiController.md b/articles/quickstart/backend/webapi-owin/files/ApiController.md new file mode 100644 index 0000000000..f2c2cd153e --- /dev/null +++ b/articles/quickstart/backend/webapi-owin/files/ApiController.md @@ -0,0 +1,42 @@ +--- +name: ApiController.cs +language: csharp +--- + +```csharp +[RoutePrefix("api")] +public class ApiController : ApiController +{ + [HttpGet] + [Route("public")] + public IHttpActionResult Public() + { + return Json(new + { + Message = "Hello from a public endpoint!" + }); + } + + [HttpGet] + [Route("private")] + [Authorize] + public IHttpActionResult Private() + { + return Json(new + { + Message = "Hello from a private endpoint! You need to be authenticated to see this." + }); + } + + [HttpGet] + [Route("private-scoped")] + [ScopeAuthorize("read:messages")] + public IHttpActionResult Scoped() + { + return Json(new + { + Message = "Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this." + }); + } +} +``` diff --git a/articles/quickstart/backend/webapi-owin/files/OpenIdConnectSigningKeyResolver.md b/articles/quickstart/backend/webapi-owin/files/OpenIdConnectSigningKeyResolver.md new file mode 100644 index 0000000000..f15111e01b --- /dev/null +++ b/articles/quickstart/backend/webapi-owin/files/OpenIdConnectSigningKeyResolver.md @@ -0,0 +1,22 @@ +--- +name: OpenIdConnectSigningKeyResolver.cs +language: csharp +--- + +```csharp +public class OpenIdConnectSigningKeyResolver +{ + private readonly OpenIdConnectConfiguration openIdConfig; + + public OpenIdConnectSigningKeyResolver(string authority) + { + var cm = new ConfigurationManager($"{authority.TrimEnd('/')}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever()); + openIdConfig = AsyncHelper.RunSync(async () => await cm.GetConfigurationAsync()); + } + + public SecurityKey[] GetSigningKey(string kid) + { + return new[] { openIdConfig.JsonWebKeySet.GetSigningKeys().FirstOrDefault(t => t.KeyId == kid) }; + } +} +``` diff --git a/articles/quickstart/backend/webapi-owin/files/ScopeAuthorizeAttribute.md b/articles/quickstart/backend/webapi-owin/files/ScopeAuthorizeAttribute.md new file mode 100644 index 0000000000..6d78b4041a --- /dev/null +++ b/articles/quickstart/backend/webapi-owin/files/ScopeAuthorizeAttribute.md @@ -0,0 +1,41 @@ +--- +name: ScopeAuthorizeAttribute.cs +language: csharp +--- + +```csharp +public class ScopeAuthorizeAttribute : AuthorizeAttribute +{ + private readonly string scope; + + public ScopeAuthorizeAttribute(string scope) + { + this.scope = scope; + } + + public override void OnAuthorization(HttpActionContext actionContext) + { + base.OnAuthorization(actionContext); + + // Get the Auth0 domain, in order to validate the issuer + var domain = $"https://{ConfigurationManager.AppSettings["Auth0Domain"]}/"; + + // Get the claim principal + ClaimsPrincipal principal = actionContext.ControllerContext.RequestContext.Principal as ClaimsPrincipal; + + // Get the scope clain. Ensure that the issuer is for the correcr Auth0 domain + var scopeClaim = principal?.Claims.FirstOrDefault(c => c.Type == "scope" && c.Issuer == domain); + if (scopeClaim != null) + { + // Split scopes + var scopes = scopeClaim.Value.Split(' '); + + // Succeed if the scope array contains the required scope + if (scopes.Any(s => s == scope)) + return; + } + + HandleUnauthorizedRequest(actionContext); + } +} +``` diff --git a/articles/quickstart/backend/webapi-owin/files/startup.md b/articles/quickstart/backend/webapi-owin/files/startup.md index 5890cc00fd..015db9db1d 100644 --- a/articles/quickstart/backend/webapi-owin/files/startup.md +++ b/articles/quickstart/backend/webapi-owin/files/startup.md @@ -2,7 +2,7 @@ name: Startup.cs language: csharp --- - + ```csharp public void Configuration(IAppBuilder app) { @@ -25,4 +25,4 @@ public void Configuration(IAppBuilder app) // Configure Web API WebApiConfig.Configure(app); } -``` \ No newline at end of file +``` diff --git a/articles/quickstart/backend/webapi-owin/interactive.md b/articles/quickstart/backend/webapi-owin/interactive.md index d5165f9df3..491a82de60 100644 --- a/articles/quickstart/backend/webapi-owin/interactive.md +++ b/articles/quickstart/backend/webapi-owin/interactive.md @@ -1,98 +1,58 @@ --- -title: Add Authorization to an ASP.NET Owin Web API application +title: Add Authorization to Your ASP.NET OWIN Web API Application description: This tutorial demonstrates how to add authorization to an ASP.NET OWIN API using the standard JWT middleware. -budicon: 448 -topics: - - quickstart - - backend - - webapi-owin -github: - path: Quickstart/Sample -contentType: tutorial -useCase: quickstart -interactive: true +interactive: true files: - - files/startup - - files/openid-connect-signing-key-resolver - - files/scope-authorize-attribute - - files/api-controller + - files/Startup + - files/OpenIdConnectSigningKeyResolver + - files/ScopeAuthorizeAttribute + - files/ApiController +github: + path: https://github.com/auth0-samples/auth0-aspnet-owin-webapi-samples/tree/master/Quickstart/Sample +locale: en-US --- -# Add Authorization to Your ASP.NET Owin Web API Application -Auth0 allows you to add authorization to any kind of application. This guide demonstrates how to integrate Auth0 with any new or existing ASP.NET Owin Web API application using the `Microsoft.Owin.Security.Jwt` package. - -If you have not created an API in your Auth0 dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API for your project. - -To set up your first API through the Auth0 dashboard, review our getting started guide. - -Each Auth0 API uses the API Identifier, which your application needs to validate the access token. +# Add Authorization to Your ASP.NET OWIN Web API Application - -<%= include('../../../_includes/_api_auth_intro') %> +

Auth0 allows you to add authorization to any kind of application. This guide demonstrates how to integrate Auth0 with any new or existing ASP.NET Owin Web API application using the Microsoft.Owin.Security.Jwt package.

If you have not created an API in your Auth0 dashboard yet, you can use the interactive selector to create a new Auth0 API or select an existing API for your project.

To set up your first API through the Auth0 dashboard, review our getting started guide.

Each Auth0 API uses the API Identifier, which your application needs to validate the access token.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

## Define permissions -<%= include('../_includes/_api_scopes_access_resources') %> -## Install dependencies - -Install the `Microsoft.Owin.Security.Jwt` NuGetPackage. This package contains the OWIN JWT Middleware necessary to use Auth0 access tokens in the ASP.NET Owin Web API. -```bash -Install-Package Microsoft.Owin.Security.Jwt -``` +

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and a write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions view of the Auth0 Dashboard's APIs section. The following example uses the read:messages scope.

Auth0 Dashboard> Applications > APIs > [Specific API] > Permissions tab

-## Configure the middleware {{{ data-action=code data-code="Startup.cs" }}} - -Go to the `Configuration` method of your `Startup` class and add a call to `UseJwtBearerAuthentication` passing in the configured `JwtBearerAuthenticationOptions`. +## Install dependencies -The `JwtBearerAuthenticationOptions` needs to specify your Auth0 API Identifier in the `ValidAudience` property, and the full path to your Auth0 domain as the `ValidIssuer`. You will need to configure the `IssuerSigningKeyResolver` to use the instance of `OpenIdConnectSigningKeyResolver` to resolve the signing key: -::: panel-warning Do not forget the trailing slash -Ensure the URL specified for `ValidIssuer` contains a trailing forward slash (`/`). This must match exactly with the JWT issuer claim. API calls will not authenticate correctly if you misconfigured this value. -::: +

Install the Microsoft.Owin.Security.Jwt NuGetPackage. This package contains the OWIN JWT Middleware necessary to use Auth0 access tokens in the ASP.NET Owin Web API.

Install-Package Microsoft.Owin.Security.Jwt
 
-## Verifying the token signature {{{ data-action=code data-code="OpenIdConnectSigningKeyResolver.cs" }}}
-The OWIN JWT middleware does not use Open ID Connect Discovery by default, so you must provide a custom `IssuerSigningKeyResolver`. Create the `OpenIdConnectSigningKeyResolver` class and ensure to return the correct `SecurityKey` by implementing `GetSigningKey`.
-This class is then used as `TokenValidationParameters.IssuerSigningKeyResolver` while configuring the middleware in `Startup.cs`.
+
-:::note -This custom resolver is deprecated and no longer available. You must provider this customer resolver yourself. -::: +

-## Validate scopes {{{ data-action=code data-code="ScopeAuthorizeAttribute.cs" }}} +## Configure the middleware {{{ data-action="code" data-code="Startup.cs" }}} -The JWT middleware verifies that the access token included in the request is valid; however, it doesn't yet include any mechanism for checking that the token has the sufficient **scope** to access the requested resources. -Create a class called `ScopeAuthorizeAttribute` which inherits from `System.Web.Http.AuthorizeAttribute`. This attribute will check that the `scope` claim issued by your Auth0 tenant is present, and if so it will ensure that the `scope` claim contains the requested scope. +

Go to the Configuration method of your Startup class and add a call to UseJwtBearerAuthentication passing in the configured JwtBearerAuthenticationOptions.

The JwtBearerAuthenticationOptions needs to specify your Auth0 API Identifier in the ValidAudience property, and the full path to your Auth0 domain as the ValidIssuer. You will need to configure the IssuerSigningKeyResolver to use the instance of OpenIdConnectSigningKeyResolver to resolve the signing key.

Do not forget the trailing slash.

Ensure the URL specified for ValidIssuer contains a trailing forward slash (/). This must match exactly with the JWT issuer claim. API calls will not authenticate correctly if you misconfigured this value.

-## Protect API endpoints {{{ data-action=code data-code="ApiController.cs" }}} +## Verify the token signature {{{ data-action="code" data-code="OpenIdConnectSigningKeyResolver.cs" }}} -<%= include('../_includes/_api_endpoints') %> -The JWT middleware integrates with the standard ASP.NET authentication and authorization mechanisms, so you only need to decorate your controller action with the `[Authorize]` attribute to secure an endpoint. +

The OWIN JWT middleware does not use Open ID Connect Discovery by default, so you must provide a custom IssuerSigningKeyResolver.

Create the OpenIdConnectSigningKeyResolver class and ensure to return the correct SecurityKey by implementing GetSigningKey. This class is then used as TokenValidationParameters.IssuerSigningKeyResolver while configuring the middleware in Startup.cs.

This custom resolver is deprecated and no longer available. You must provide this custom resolver yourself.

-Update the action with the `ScopeAuthorize` attribute and pass the name of the required `scope` in the `scope` parameter. This ensures the correct scope is available to call a specific API endpoing. +## Validate scopes {{{ data-action="code" data-code="ScopeAuthorizeAttribute.cs" }}} -::::checkpoint -:::checkpoint-default +

The JWT middleware verifies that the access token included in the request is valid; however, it doesn't yet include any mechanism for checking that the token has the sufficient scope to access the requested resources.

Create a class called ScopeAuthorizeAttribute which inherits from System.Web.Http.AuthorizeAttribute. This attribute will check that the scope claim issued by your Auth0 tenant is present, and if so, it will ensure that the scope claim contains the requested scope.

-Now that you have configured your application, run your application to verify that: -* `GET /api/public` is available for non-authenticated requests. -* `GET /api/private` is available for authenticated requests. -* `GET /api/private-scoped` is available for authenticated requests containing an access token with the `read:messages` scope. +## Protect API endpoints {{{ data-action="code" data-code="ApiController.cs" }}} -::: -:::checkpoint-failure -If your application did not start successfully: -* Ensure your configured the `ValidIssuer` and `ValidAudience` values correctly -* Verify you added the token as the `Authorization` header -* Ensure the token has the correct scopes. Verify with jwt.io. +

The routes shown below are available for the following requests:

  • GET /api/public: Available for non-authenticated requests.

  • GET /api/private: Available for authenticated requests containing an access token with no additional scopes.

  • GET /api/private-scoped: Available for authenticated requests containing an access token with the read:messages scope granted.

The JWT middleware integrates with the standard ASP.NET authentication and authorization mechanisms, so you only need to decorate your controller action with the [Authorize] attribute to secure an endpoint.

Update the action with the ScopeAuthorize attribute and pass the name of the required scope in the scope parameter. This ensures the correct scope is available to call a specific API endpoint.

ASP.NET API OWIN Quickstart - Step 6 Checkpoint

Now that you have configured your application, run your application and verify that:

  • GET /api/public is available for non-authenticated requests.

  • GET /api/private is available for authenticated requests.

  • GET /api/private-scoped is available for authenticated requests containing an access token with the read:messages scope.

-Still having issues? Check out our documentation or visit our community page to get more help. +
-::: +

If your application did not start successfully:

  • Ensure your configured the ValidIssuer and ValidAudience values correctly

  • Verify you added the token as the Authorization header

  • Ensure the token has the correct scopes. Verify with jwt.io.

Still having issues? Check out our documentation or visit our community page to get more help.

-:::: +

diff --git a/articles/quickstart/native/android-facebook-login/files/exchangeTokens.md b/articles/quickstart/native/android-facebook-login/files/exchangeTokens.md new file mode 100644 index 0000000000..c2593142a7 --- /dev/null +++ b/articles/quickstart/native/android-facebook-login/files/exchangeTokens.md @@ -0,0 +1,26 @@ +--- +name: exchangeTokens +language: kotlin +--- + +```kotlin +private fun exchangeTokens( + sessionToken: String, + userProfile: String, + callback: SimpleCallback + ) { + val params = mapOf("user_profile" to userProfile) + apiClient.loginWithNativeSocialToken(sessionToken, FACEBOOK_SUBJECT_TOKEN_TYPE) + .setScope(AUTH0_SCOPE) + .addParameters(params) + .start(object : Callback { + override fun onFailure(error: AuthenticationException) { + callback.onError(error) + } + + override fun onSuccess(result: Credentials) { + callback.onResult(result) + } + }) + } +``` diff --git a/articles/quickstart/native/android-facebook-login/files/fetchSessionToken.md b/articles/quickstart/native/android-facebook-login/files/fetchSessionToken.md new file mode 100644 index 0000000000..24232d1f4c --- /dev/null +++ b/articles/quickstart/native/android-facebook-login/files/fetchSessionToken.md @@ -0,0 +1,35 @@ +--- +name: fetchSessionToken +language: kotlin +--- + +```kotlin +private fun fetchSessionToken(token: String, callback: SimpleCallback) { + val params = Bundle().apply { + putString("grant_type", "fb_attenuate_token") + putString("fb_exchange_token", token) + putString("client_id", getString(R.string.facebook_app_id)) + } + + val request = GraphRequest().apply { + parameters = params + graphPath = "oauth/access_token" + } + + request.callback = GraphRequest.Callback { response -> + if (response.error != null) { + callback.onError(response.error?.exception!!) + return@Callback + } + + try { + val fbSessionToken = response.jsonObject!!.getString("access_token") + callback.onResult(fbSessionToken) + } catch (jsonException: JSONException) { + //Failed to parse session token + callback.onError(jsonException) + } + } + request.executeAsync() + } +``` diff --git a/articles/quickstart/native/android-facebook-login/files/fetchUserProfile.md b/articles/quickstart/native/android-facebook-login/files/fetchUserProfile.md new file mode 100644 index 0000000000..d54981f1d4 --- /dev/null +++ b/articles/quickstart/native/android-facebook-login/files/fetchUserProfile.md @@ -0,0 +1,32 @@ +--- +name: fetchUserProfile +language: kotlin +--- + +```kotlin +private fun fetchUserProfile(token: String, userId: String, callback: SimpleCallback) { + + val params = Bundle().apply { + putString("access_token", token) + putString("fields", "first_name,last_name,email") + } + + val request = GraphRequest().apply { + parameters = params + graphPath = userId + } + + request.callback = GraphRequest.Callback { response -> + val error = response.error + if (error != null) { + //Failed to fetch user profile + callback.onError(error.exception!!) + return@Callback + } + //Handle back the profile as received + callback.onResult(response.rawResponse!!) + } + + request.executeAsync() + } +``` diff --git a/articles/quickstart/native/android-facebook-login/files/onCreate.md b/articles/quickstart/native/android-facebook-login/files/onCreate.md new file mode 100644 index 0000000000..1102e17fb5 --- /dev/null +++ b/articles/quickstart/native/android-facebook-login/files/onCreate.md @@ -0,0 +1,41 @@ +--- +name: onCreate +language: kotlin +--- + +```kotlin +override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + FacebookSdk.setClientToken(getString(R.string.facebook_client_token)) + FacebookSdk.sdkInitialize(this) + + setContentView(R.layout.activity_login) + + val auth0 = + Auth0(getString(R.string.com_auth0_client_id), getString(R.string.com_auth0_domain)) + apiClient = AuthenticationAPIClient(auth0) + + fbCallbackManager = CallbackManager.Factory.create() + + val loginButton = findViewById(R.id.login_button).apply { + setPermissions(*FACEBOOK_PERMISSIONS) + } + + loginButton.registerCallback(fbCallbackManager, object : FacebookCallback { + override fun onSuccess(result: LoginResult) { + + val accessToken = result.accessToken + performLogin(accessToken) + } + + override fun onCancel() { + Log.i(TAG, "Facebook sign-in cancelled") + } + + override fun onError(error: FacebookException) { + Log.e(TAG, "Error {error.message}") + } + }) + } +``` diff --git a/articles/quickstart/native/android-facebook-login/files/performLogin + SimpleCallback.md b/articles/quickstart/native/android-facebook-login/files/performLogin + SimpleCallback.md new file mode 100644 index 0000000000..6645fdf0d8 --- /dev/null +++ b/articles/quickstart/native/android-facebook-login/files/performLogin + SimpleCallback.md @@ -0,0 +1,12 @@ +--- +name: performLogin + SimpleCallback +language: kotlin +--- + +```kotlin +private interface SimpleCallback { + fun onResult(result: T) + + fun onError(cause: Throwable) +} +``` diff --git a/articles/quickstart/native/android-facebook-login/files/performLogin.md b/articles/quickstart/native/android-facebook-login/files/performLogin.md new file mode 100644 index 0000000000..84a6251bd1 --- /dev/null +++ b/articles/quickstart/native/android-facebook-login/files/performLogin.md @@ -0,0 +1,41 @@ +--- +name: performLogin +language: kotlin +--- + +```kotlin +private fun performLogin(accessToken: AccessToken) { + val token = accessToken.token + fetchSessionToken(token, object : SimpleCallback { + override fun onResult(sessionToken: String) { + //2. Obtained the Facebook session token + fetchUserProfile(token, accessToken.userId, object : SimpleCallback { + override fun onResult(userProfile: String) { + //3. Obtained the user profile + exchangeTokens( + sessionToken, + userProfile, + object : SimpleCallback { + override fun onResult(credentials: Credentials) { + //4. Exchanged the tokens + Log.i(TAG, "Logged in to Auth0") + } + + override fun onError(cause: Throwable) { + Log.e(TAG, "Error exchanging tokens", cause) + } + }) + } + + override fun onError(cause: Throwable) { + Log.e(TAG, "Error fetching the profile", cause) + } + }) + } + + override fun onError(cause: Throwable) { + Log.e(TAG, "Error fetching the session token", cause) + } + }) + } +``` diff --git a/articles/quickstart/native/android-facebook-login/index.yml b/articles/quickstart/native/android-facebook-login/index.yml index 28ad3647d3..c4db748f26 100644 --- a/articles/quickstart/native/android-facebook-login/index.yml +++ b/articles/quickstart/native/android-facebook-login/index.yml @@ -28,6 +28,8 @@ snippets: default_article: 00-login-facebook articles: - 00-login-facebook +hidden_articles: + - interactive next_steps: - path: 00-login-facebook list: diff --git a/articles/quickstart/native/android-facebook-login/interactive.md b/articles/quickstart/native/android-facebook-login/interactive.md new file mode 100644 index 0000000000..b7e9bc7d23 --- /dev/null +++ b/articles/quickstart/native/android-facebook-login/interactive.md @@ -0,0 +1,129 @@ +--- +title: Android - Facebook Login +description: This tutorial demonstrates how to add user login to an Android application using native Facebook Login. +interactive: true +files: + - files/performLogin + SimpleCallback + - files/onCreate + - files/fetchSessionToken + - files/fetchUserProfile + - files/exchangeTokens + - files/performLogin +github: + path: https://github.com/auth0-samples/auth0-android-native-social-sample/tree/master/00-login-facebook +locale: en-US +--- + +# Android - Facebook Login + + +

This tutorial demonstrates how to add user login to an Android application using native Facebook Login. We recommend that you log in to follow this quickstart with examples configured for your account.

System requirements 

  • Android Studio 3.6.1

  • Android SDK 25

  • Emulator - Nexus 5X - Android 6.0

This tutorial describes how to implement login with the Facebook SDK.​

Before You Start

Configure your Auth0 application in the dashboard to use Facebook Native Sign In. See Add Facebook Login to Native Apps. When you finish this step, your application will be able to implement Facebook Native Login.

+ +## Request Facebook permissions + + +

Your application is already able to sign in with Facebook. However, to ensure you have a rich user profile, you need to update the permissions with which the Facebook Login Button was set up.

Set the requested permissions to public_profile and email. This way, the user email will also be included as part of the response, provided the access request is accepted by the user.

loginButton.setPermissions(Arrays.asList("public_profile", "email"));

+ +## Create performLogin method {{{ data-action="code" data-code="performLogin + SimpleCallback" }}} + + +

Now, to kick off the authentication process with Auth0, create a new method in which you will prepare the payload to be sent.

You will make use of a small interface to handle our internal callbacks.

In the sample, the method was named performLogin and the interface SimpleCallback. Go ahead and add both.

+ +## Call performLogin method {{{ data-action="code" data-code="onCreate" }}} + + +

Now, call the method from the Facebook login callback's onSuccess method.

+ +## Integrate Facebook + + +

When you sign in with Facebook at Auth0, the backend will perform some checks in the background to ensure the user is who they say they are. To achieve this, it needs to be provided with a Session Access Token.

Furthermore, if a user needs to be created on Auth0 to represent this Facebook user, the backend will require some of their information, such as their name, last name, and email. The email, if provided, will be flagged as non-verified on the Auth0 user profile.

To obtain the Session Access Token and the user profile, two additional requests need to be made against the Facebook API.

+ +## Fetch Facebook session Access Token {{{ data-action="code" data-code="fetchSessionToken" }}} + + +

Make a new GET request against the Facebook API's /oauth/access_token endpoint. Use the following query parameters:

  • grant_type: fb_attenuate_token.

  • fb_exchange_token: the access token received upon login.

  • client_id: your App ID. This value comes from the Facebook Developer's dashboard and should already be in use in your application if you have integrated Facebook Login successfully.

Put the logic from this step in its own method. You will be calling it later from the previously-added method.

The sample uses the Facebook SDK's GraphRequest class to perform this request.

+ +## Fetch Facebook user profile {{{ data-action="code" data-code="fetchUserProfile" }}} + + +

Now make another GET request, just like in the step above. The endpoint path will be the User ID value from the Facebook login result (for example, /904636746222815). Use the following parameters:

  • access_token: the access token received upon login.

  • fields: the fields from the user profile that you'd like to get back in the response. These are directly tied to the Facebook Login Button permissions that were configured at the beginning. When a permission is optional, the user must first consent to give access to it. For the purpose of signing up a user at Auth0, their full name and email will suffice.

+ +## Integrate Auth0 + + +

Now that the required artifacts have been obtained, you are ready to trade them for Auth0 user credentials, such as the ID and Access Tokens. But first, you must set up the Auth0 SDK to make that last request.

Get your application keys

  1. Go to the Applications section of the Auth0 Dashboard and select the existing application in which you enabled Sign in with Facebook. If you need help with this step, please check the requirements section at the top of this article.

  2. Copy the Domain and Client ID values from the application settings page. These are required by the SDK.

  3. Create two new resources in your Android application's strings.xml file to store them. The name of the keys must match the ones used below: + +

    <resources>
    +
    +    <string name="com_auth0_domain">${account.namespace}</string>
    +
    +    <string name="com_auth0_client_id">${account.clientId}</string>
    +
    +</resources>
    +
    +
    + +

Install the Auth0 SDK

In your Android application, add this line to the app/build.gradle file:

dependencies {
+
+    implementation 'com.auth0.android:auth0:1.+'
+
+}
+
+
+ +

Now is time to run the Gradle Sync task to refresh the project and its dependencies.

Update manifest for web authentication

If your application does not plan to make use of the Web Authentication module provided by the SDK, you will need to remove the unused activity from the AndroidManifest.xml file to prevent Manifest Placeholder issues. This can be achieved by adding an activity declaration and annotating it with tools:node="remove".

<application>
+
+  <!-- Add the activity declaration line below -->
+
+   <activity
+
+    android:name="com.auth0.android.provider.AuthenticationActivity"
+
+    tools:node="remove" />
+
+
+
+</application>
+
+
+ +

However, if you do plan to support Web Authentication, head over here to learn how to declare the Manifest Placeholders.

+ +## Exchange the received data for Auth0 tokens {{{ data-action="code" data-code="exchangeTokens" }}} + + +

The SDK must be instantiated before use. Define a field at the class level and initialize it on the onCreate method. Note how the credentials defined in the step above are passed to the Auth0 constructor and then a new instance of the AuthenticationAPIClient is created with it.

private AuthenticationAPIClient auth0Client;
+
+
+
+@Override
+
+public void onCreate(Bundle savedInstanceState) {
+
+    super.onCreate(savedInstanceState);
+
+
+
+    setContentView(R.layout.activity_login);
+
+
+
+    Auth0 account = new Auth0(getString(R.string.com_auth0_client_id), getString(R.string.com_auth0_domain));
+
+    auth0Client = new AuthenticationAPIClient(account);
+
+
+
+    //...
+
+}
+
+
+ +

Create the method that will hold the logic to exchange the two obtained artifacts for Auth0 user credentials. In the sample, this method is named exchangeTokens.

The API client declares the method loginWithNativeSocialToken that receives a token and a subject type. The former corresponds to the session token, and the latter indicates what type of connection the backend will attempt to authenticate with.

For native Facebook Login, you will use the following value: "http://auth0.com/oauth/token-type/facebook-info-session-access-token"

Other values that need to be configured are the user profile (using the user_profile key) and the scope you request the Auth0 tokens contain.

It's a good practice to keep all the values that you know won't change as constants at the top of the class. The sample makes use of constants for the subject token type, the Facebook permissions, and the Auth0 scopes. You can read more about Auth0 scopes in the dedicated article.

+ +## Update performLogin method {{{ data-action="code" data-code="performLogin" }}} + + +

Now that every step is defined in its own method, it's time to put everything together inside the performLogin method.

If everything went well, you should now be able to authenticate natively with the Facebook Login SDK. This means that if the Facebook app is installed on the device, the authentication will be handled via the application and not a browser app.

diff --git a/articles/quickstart/native/android/files/MainActivity.md b/articles/quickstart/native/android/files/MainActivity.md new file mode 100644 index 0000000000..2e476f1606 --- /dev/null +++ b/articles/quickstart/native/android/files/MainActivity.md @@ -0,0 +1,78 @@ +--- +name: MainActivity.kt +language: kotlin +--- + +```kotlin +import com.auth0.android.Auth0 +import com.auth0.android.provider.WebAuthProvider + +class MainActivity : AppCompatActivity() { + + private lateinit var account: Auth0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Set up the account object with the Auth0 application details + account = Auth0( + getString(R.string.com_auth0_client_id), + getString(R.string.com_auth0_domain), + ) + } + + private fun loginWithBrowser() { + // Setup the WebAuthProvider, using the custom scheme and scope. + + WebAuthProvider.login(account) + .withScheme("demo") + .withScope("openid profile email") + // Launch the authentication passing the callback where the results will be received + .start(this, object : Callback { + // Called when there is an authentication failure + override fun onFailure(exception: AuthenticationException) { + // Something went wrong! + } + + // Called when authentication completed successfully + override fun onSuccess(credentials: Credentials) { + // Get the access token from the credentials object. + // This can be used to call APIs + val accessToken = credentials.accessToken + } + }) + } + + private fun logout() { + WebAuthProvider.logout(account) + .withScheme("demo") + .start(this, object : Callback { + override fun onSuccess(payload: Void?) { + // The user has been logged out! + } + + override fun onFailure(error: AuthenticationException) { + // Something went wrong! + } + }) + } + + private fun showUserProfile(accessToken: String) { + var client = AuthenticationAPIClient(account) + + // With the access token, call `userInfo` and get the profile from Auth0. + client.userInfo(accessToken) + .start(object : Callback { + override fun onFailure(exception: AuthenticationException) { + // Something went wrong! + } + + override fun onSuccess(profile: UserProfile) { + // We have the user's profile! + val email = profile.email + val name = profile.name + } + }) + } +} +``` diff --git a/articles/quickstart/native/android/files/build.md b/articles/quickstart/native/android/files/build.md index 6ab782ff49..949546a282 100644 --- a/articles/quickstart/native/android/files/build.md +++ b/articles/quickstart/native/android/files/build.md @@ -1,8 +1,9 @@ --- name: build.gradle -language: groovy +language: javascript --- -```groovy + +```javascript apply plugin: 'com.android.application' android { @@ -22,4 +23,4 @@ dependencies { // Add the Auth0 Android SDK implementation 'com.auth0.android:auth0:2.+' } -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/android/files/strings.md b/articles/quickstart/native/android/files/strings.md index 58186175c2..b83b0540e1 100644 --- a/articles/quickstart/native/android/files/strings.md +++ b/articles/quickstart/native/android/files/strings.md @@ -1,10 +1,11 @@ --- name: strings.xml -language: xml +language: --- -```xml + +``` - ${account.namespace} - ${account.clientId} + "${account.namespace}" + "${account.clientId}" ``` diff --git a/articles/quickstart/native/android/interactive.md b/articles/quickstart/native/android/interactive.md index 5969faffc2..7e20f59ce0 100644 --- a/articles/quickstart/native/android/interactive.md +++ b/articles/quickstart/native/android/interactive.md @@ -1,74 +1,59 @@ --- -title: Add Login to your Android App -description: This quickstart demonstrates how to add user login to an Android application using Auth0. -seo_alias: android -interactive: true +title: Add Login to Your Android Application +description: This guide demonstrates how to integrate Auth0 with a Android application using the Auth0 Android SDK. +interactive: true files: - - files/build - - files/strings - - files/main + - files/build + - files/strings + - files/MainActivity github: - path: 00-Login-Kt + path: https://github.com/auth0-samples/auth0-android-sample/tree/master/00-Login-Kt +locale: en-US --- # Add Login to Your Android Application - - +

-## Configure Auth0 {{{ data-action=configure }}} +## Configure Auth0 -To use Auth0 services, you need to have an application set up in the Auth0 Dashboard. The Auth0 application is where you will configure authentication in your project. -### Configure an application -Use the interactive selector to create a new Auth0 application or select an existing application that represents the project you want to integrate with. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code will use to call Auth0 APIs through the SDK. +

To use Auth0 services, you need to have an application set up in the Auth0 Dashboard. The Auth0 application is where you will configure authentication in your project.

Configure an application

Use the interactive selector to create a new Auth0 application or select an existing application that represents the project you want to integrate with. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code will use to call Auth0 APIs through the SDK.

Any settings you configure using this quickstart will automatically update for your Application in the Dashboard, which is where you can manage your Applications in the future.

If you would rather explore a complete configuration, you can view a sample application instead.

Configure callback URLs

A callback URL is the application URL that Auth0 will direct your users to once they have authenticated. If you do not set this value, Auth0 will not return users to your application after they log in.

If you are following along with our sample project, set this to demo://{yourDomain}/android/YOUR_APP_PACKAGE_NAME/callback.

Configure logout URLs

A logout URL is the application URL Auth0 will redirect your users to once they log out. If you do not set this value, users will not be able to log out from your application and will receive an error.

If you are following along with our sample project, set this to demo://{yourDomain}/android/YOUR_APP_PACKAGE_NAME/callback

-Any settings you configure using this quickstart will automatically update for your Application in the Dashboard, which is where you can manage your Applications in the future. +## Install the Auth0 Android SDK {{{ data-action="code" data-code="build.gradle#18:18" }}} -If you would rather explore a complete configuration, you can view a sample application instead. -### Configure callback URLs +

Add the Auth0 Android SDK into your project. The library will make requests to the Auth0's Authentication and Management APIs.

In your app's build.gradle dependencies section, add the following:

implementation 'com.auth0.android:auth0:2. '
 
-A callback URL is the application URL that Auth0 will direct your users to once they have authenticated. If you do not set this value, Auth0 will not return users to your application after they log in.
+
-::: note -If you are following along with our sample project, set this to `demo://${account.namespace}/android/YOUR_APP_PACKAGE_NAME/callback`. -::: +

Ensure you target Java 8+ byte code for Android and Kotlin plugins respectively.

-### Configure logout URLs +## Add manifest placeholders {{{ data-action="code" data-code="build.gradle#10:12" }}} -A logout URL is the application URL Auth0 will redirect your users to once they log out. If you do not set this value, users will not be able to log out from your application and will receive an error. -::: note -If you are following along with our sample project, set this to `demo://${account.namespace}/android/YOUR_APP_PACKAGE_NAME/callback`. -::: +

The SDK requires manifest placeholders. Auth0 uses placeholders internally to define an intent-filter, which captures the authentication callback URL. You must set Auth0 tenant domain and the callback URL scheme.

You do not need to declare a specific intent-filter for your activity, because you have defined the manifest placeholders with your Auth0 Domain and Scheme values and the library will handle the redirection for you.

We've used a value of demo for auth0Scheme here, so that a custom URL scheme can be used for the URL that Auth0 redirects to after login. The alternative is https if you want to use Android App Links. You can read more about setting this value in the Auth0.Android SDK README.

-## Install the Auth0 Android SDK {{{ data-action=code data-code="build.gradle#18" }}} +## Configure your application {{{ data-action="code" data-code="strings.xml#2:3" }}} -Add the Auth0 Android SDK into your project. The library will make requests to the Auth0's Authentication and Management APIs. -In your app's `build.gradle` dependencies section, add the following: +

For the SDK to function properly, you must set the following properties in strings.xml:

  • com_auth0_domain: The domain of your Auth0 tenant. Generally, you can find this in the Auth0 Dashboard under your Application's Settings in the Domain field. If you are using a custom domain, you should set this to the value of your custom domain instead.

  • com_auth0_client_id: The ID of the Auth0 Application you set up earlier in this quickstart. You can find this in the Auth0 Dashboard under your Application's Settings in the Client ID field.

Ensure that the AndroidManifest.xml file specifies the android.permissions.INTERNET permission:

<uses-permission android:name="android.permission.INTERNET" />
 
-```groovy
-implementation 'com.auth0.android:auth0:2.+'
-```
+
-Ensure you target Java 8+ byte code for Android and Kotlin plugins respectively. +

Run Sync Project with Gradle Files inside Android Studio or execute ./gradlew clean assembleDebug from the command line.

For more information about using Gradle, check the Gradle official documentation.

-## Add manifest placeholders {{{ data-action=code data-code="build.gradle#10:12" }}} +## Add login to your application {{{ data-action="code" data-code="MainActivity.kt#6:38" }}} -The SDK requires manifest placeholders. Auth0 uses placeholders internally to define an `intent-filter`, which captures the authentication callback URL. You must set Auth0 tenant domain and the callback URL scheme. -You do not need to declare a specific `intent-filter` for your activity, because you have defined the manifest placeholders with your Auth0 **Domain** and **Scheme** values and the library will handle the redirection for you. +

Universal Login is the easiest way to set up authentication in your application. We recommend using it for the best experience, best security and the fullest array of features.

In the onCreate method, create a new instance of the Auth0 class to hold user credentials.

Create a loginWithBrowser method and use the WebAuthProvider class to authenticate with any connection you enabled on your application in the Auth0 dashboard. Here, you can pass the scheme value that was used in the auth0Scheme manifest placeholder as part of the initial configuration.

After you call the WebAuthProvider#start function, the browser launches and shows the login page. Once the user authenticates, the callback URL is called. The callback URL contains the final result of the authentication process.

Android Quickstart step 5 checkpoint

Add a button to your application that calls loginWithBrowser. When you click it, verify that your Android application redirects you to the Auth0 Universal Login page and that you can now log in or sign up using a username and password or a social provider.

Once that's complete, verify that Auth0 redirects back to your app.

-::: note -We've used a value of `demo` for `auth0Scheme` here, so that a custom URL scheme can be used for the URL that Auth0 redirects to after login. The alternative is `https` if you want to use Android App Links. You can read more about setting this value in the Auth0.Android SDK README. -::: +
-## Configure your application {{{ data-action=code data-code="strings.xml#2:3" }}} +

If your application did not launch successfully:

  • Ensure you set the Allowed Callback URLs are correct

  • Verify you saved your changes after entering your URLs

  • Make sure the domain and cliend ID values imported correctly

Still having issues? Check out our documentation or visit our community page to get more help.

-For the SDK to function properly, you must set the following properties in `strings.xml`: +

- `com_auth0_domain`: The domain of your Auth0 tenant. Generally, you can find this in the Auth0 Dashboard under your Application's Settings in the Domain field. If you are using a custom domain, you should set this to the value of your custom domain instead. - `com_auth0_client_id`: The ID of the Auth0 Application you set up earlier in this quickstart. You can find this in the Auth0 Dashboard under your Application's Settings in the Client ID field. @@ -142,33 +127,13 @@ Still having issues? Check out our retrieve the users profile from Auth0. This requires: +## Show user profile information {{{ data-action="code" data-code="MainActivity.kt#54:70" }}} -- The access token returned from the login phase -- The `WebAuthProvider.login` must contain the `profile` scope -You must specify the `email` scope if you need to retreive the user's email address. +

Use the AuthenticationAPIClient class to retrieve the user's profile from Auth0. This requires:

  • The access token returned from the login phase

  • The WebAuthProvider.login must contain the profile scope

You must specify the email scope if you need to retrieve the user's email address.

This quickstart sets the openid profile email scopes by default during the login step above.

The following demonstrates a function that can be used to retrieve the user's profile and show it on the screen:

Android Quickstart step 7 checkpoint

Call the showUserProfile function after login. Verify the onSuccess callback returns the user's profile information.

-:::note -This quickstart sets the `openid profile email` scopes by default during the login step above. -::: +
-The following demonstrates a function that can be used to retrieve the user's profile and show it on the screen: - -::::checkpoint +

If your application did not return user profile information:

  • Verify the accessToken is valid

Still having issues? Check out our documentation or visit our community page to get more help.

-:::checkpoint-default -Call the `showUserProfile` function after login. Verify the `onSuccess` callback returns the user's profile information. - -::: - -:::checkpoint-failure -If your application did not return user profile information: -* Verify the `accessToken` is valid - -Still having issues? Check out our documentation or visit our community page to get more help. - -::: -:::: +

diff --git a/articles/quickstart/native/device/index.yml b/articles/quickstart/native/device/index.yml index 22ff3ff856..f253fc43c5 100644 --- a/articles/quickstart/native/device/index.yml +++ b/articles/quickstart/native/device/index.yml @@ -21,4 +21,6 @@ seo_alias: device default_article: 01-login articles: - 01-login +hidden_articles: + - interactive show_steps: false diff --git a/articles/quickstart/native/device/interactive.md b/articles/quickstart/native/device/interactive.md new file mode 100644 index 0000000000..78e12e8551 --- /dev/null +++ b/articles/quickstart/native/device/interactive.md @@ -0,0 +1,561 @@ +--- +title: Device Authorization Flow +description: This tutorial demonstrates how to call your API from an input-constrained device using the Device Authorization Flow. +interactive: true + +github: + path: https://auth0.github.io/device-flow-playground/ +locale: en-US +--- + +# Device Authorization Flow + + +

This tutorial demonstrates how to call your API from an input-constrained device using the Device Authorization Flow. We recommend that you log in to follow this quickstart with examples configured for your account.

For an interactive experience, you can use the Device Flow Playground.

Prerequisites

+ +## Request device code + + +

When the user starts the device application and wants to authorize it, your application must request a device code from the Auth0 Authentication API to associate with the user session.

To get the device code, your application must call the Authentication API Device Authorization Flow Authorize endpoint:


+
+
+ + + + + + + +
curl --request post \
+
+  --url 'https://${account.namespace}/oauth/device/code' \
+
+  --header 'content-type: application/x-www-form-urlencoded'
var client = new RestClient("https://${account.namespace}/oauth/device/code");
+
+var request = new RestRequest(Method.POST);
+
+request.AddHeader("content-type", "application/x-www-form-urlencoded");
+
+IRestResponse response = client.Execute(request);
package main
+
+
+
+import (
+
+ "fmt"
+
+ "net/http"
+
+ "io/ioutil"
+
+)
+
+
+
+func main() {
+
+
+
+ url := "https://${account.namespace}/oauth/device/code"
+
+
+
+ req, _ := http.NewRequest("post", url, nil)
+
+
+
+ req.Header.Add("content-type", "application/x-www-form-urlencoded")
+
+
+
+ res, _ := http.DefaultClient.Do(req)
+
+
+
+ defer res.Body.Close()
+
+ body, _ := ioutil.ReadAll(res.Body)
+
+
+
+ fmt.Println(res)
+
+ fmt.Println(string(body))
+
+
+
+}
HttpResponse<String> response = Unirest.post("https://${account.namespace}/oauth/device/code")
+
+  .header("content-type", "application/x-www-form-urlencoded")
+
+  .asString();
var axios = require("axios").default;
+
+
+
+var options = {
+
+  method: 'post',
+
+  url: 'https://${account.namespace}/oauth/device/code',
+
+  headers: {'content-type': 'application/x-www-form-urlencoded'}
+
+};
+
+
+
+axios.request(options).then(function (response) {
+
+  console.log(response.data);
+
+}).catch(function (error) {
+
+  console.error(error);
+
+});
#import <Foundation/Foundation.h>
+
+
+
+NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" };
+
+
+
+NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://${account.namespace}/oauth/device/code"]
+
+                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
+
+                                                   timeoutInterval:10.0];
+
+[request setHTTPMethod:@"post"];
+
+[request setAllHTTPHeaderFields:headers];
+
+
+
+NSURLSession *session = [NSURLSession sharedSession];
+
+NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
+
+                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+
+                                                if (error) {
+
+                                                    NSLog(@"%@", error);
+
+                                                } else {
+
+                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
+
+                                                    NSLog(@"%@", httpResponse);
+
+                                                }
+
+                                            }];
+
+[dataTask resume];
$curl = curl_init();
+
+
+
+curl_setopt_array($curl, [
+
+  CURLOPT_URL => "https://${account.namespace}/oauth/device/code",
+
+  CURLOPT_RETURNTRANSFER => true,
+
+  CURLOPT_ENCODING => "",
+
+  CURLOPT_MAXREDIRS => 10,
+
+  CURLOPT_TIMEOUT => 30,
+
+  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+
+  CURLOPT_CUSTOMREQUEST => "post",
+
+  CURLOPT_HTTPHEADER => [
+
+    "content-type: application/x-www-form-urlencoded"
+
+  ],
+
+]);
+
+
+
+$response = curl_exec($curl);
+
+$err = curl_error($curl);
+
+
+
+curl_close($curl);
+
+
+
+if ($err) {
+
+  echo "cURL Error #:" . $err;
+
+} else {
+
+  echo $response;
+
+}
import http.client
+
+
+
+conn = http.client.HTTPSConnection("")
+
+
+
+headers = { 'content-type': "application/x-www-form-urlencoded" }
+
+
+
+conn.request("post", "/${account.namespace}/oauth/device/code", headers=headers)
+
+
+
+res = conn.getresponse()
+
+data = res.read()
+
+
+
+print(data.decode("utf-8"))
require 'uri'
+
+require 'net/http'
+
+require 'openssl'
+
+
+
+url = URI("https://${account.namespace}/oauth/device/code")
+
+
+
+http = Net::HTTP.new(url.host, url.port)
+
+http.use_ssl = true
+
+http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+
+
+
+request = Net::HTTP::Post.new(url)
+
+request["content-type"] = 'application/x-www-form-urlencoded'
+
+
+
+response = http.request(request)
+
+puts response.read_body
import Foundation
+
+
+
+let headers = ["content-type": "application/x-www-form-urlencoded"]
+
+
+
+let request = NSMutableURLRequest(url: NSURL(string: "https://${account.namespace}/oauth/device/code")! as URL,
+
+                                        cachePolicy: .useProtocolCachePolicy,
+
+                                    timeoutInterval: 10.0)
+
+request.httpMethod = "post"
+
+request.allHTTPHeaderFields = headers
+
+
+
+let session = URLSession.shared
+
+let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
+
+  if (error != nil) {
+
+    print(error)
+
+  } else {
+
+    let httpResponse = response as? HTTPURLResponse
+
+    print(httpResponse)
+
+  }
+
+})
+
+
+
+dataTask.resume()
+ + + +

+ +## Receive device code + + +

The device application should receive an HTTP 200 response and a payload similar to this:

{
+
+  "device_code": "GmRh...k9eS",
+
+  "user_code": "WDJB-MJHT",
+
+  "verification_uri": "https://my-tenant.auth0.com/device",
+
+  "verification_uri_complete": "https://my-tenant.auth0.com/device?user_code=WDJB-MJHT",
+
+  "expires_in": 900,
+
+  "interval": 5
+
+}
+
+
+ +

+ +## Request device activation + + +

After your device application receives the device_code and the user_code, it should instruct the user to go to the verification_uri and enter the user_code.

The device_code is not intended for the user directly and should not be displayed during the interaction to avoid confusing the user.

When building a CLI, you could skip this step and immediately open the browser with verification_uri_complete.

+ +## Poll the token endpoint + + +

While your device application waits for the user to activate it, it should call the Authentication API POST /oauth/token endpoint intermittently and handle the response appropriately.

Ensure your device application waits for the length of the interval (in seconds) or until it receives a successful response, whichever is longer, to avoid network latency issues.

curl --request POST \ 
+
+--url 'https://${account.namespace}/oauth/token' \ 
+
+--header 'content-type: application/x-www-form-urlencoded' \ 
+
+--data grant_type=urn:ietf:params:oauth:grant-type:device_code \ 
+
+--data device_code=__AUTH0_SCOPES__ \ 
+
+--data 'client_id=${account.clientId}'
+
+
+ +

+ +## User authorization + + +

The user will either scan the QR code, or else will open the activation page and enter the user code:

A confirmation page will be shown to have the user confirm that this is the right device:

The user will complete the transaction by signing in. This step may include one or more of the following processes:

  • Authenticating the user

  • Redirecting the user to an Identity Provider to handle authentication

  • Checking for active SSO sessions

  • Obtaining user consent for the device, unless consent has been previously given

Upon successful authentication and consent, the confirmation prompt will be shown:

At this point, the user has authenticated and the device has been authorized.

+ +## Receive tokens + + +

After the user authorizes the device application, it receives an HTTP 200 response and the following payload:

{
+
+  "access_token": "eyJz93a...k4laUWw",
+
+  "refresh_token": "GEbRxBN...edjnXbL",
+
+  "id_token": "eyJ0XAi...4faeEoQ",
+
+  "token_type": "Bearer",
+
+  "expires_in": 86400
+
+}
+
+
+ +

You should validate your tokens before saving them. To learn how, read Validate Access Tokens and Validate ID Tokens.

Access tokens are used to call the Authentication API Get User Info endpoint (if your device application requested the openid scope) or the API that was specified by the audience parameter. If you are calling your own API, your device application must verify the access token before using it.

ID tokens contain user information that must be decoded and extracted. The Authentication API only returns an id_token if your device application requested the openid scope.

Refresh tokens are used to obtain a new access token or ID token after the previous one has expired. The Authentication API only returns a refresh_token if the Allow Offline Access setting is enabled for the API specified by the audience parameter, and your device application requested the offline_access scope.

+ +## Call your API + + +

To call your API, your device application must pass the access token as a Bearer token in the Authorization header of your HTTP request.

curl --request GET \
+
+  --url https://myapi.com/api \
+
+  --header 'authorization: Bearer __AUTH0_API_ACCESS_TOKEN__' \
+
+  --header 'content-type: application/json'
+
+
+ +

+ +## Refresh tokens + + +

To get a new access token for a user, your device application can call the Authentication API POST /oauth/token endpoint with the refresh_token parameter.

curl --request POST \
+
+  --url 'https://${account.namespace}/oauth/token' \
+
+  --header 'content-type: application/x-www-form-urlencoded' \
+
+  --data grant_type=refresh_token \
+
+  --data 'client_id=${account.clientId}' \
+
+  --data 'client_secret=${account.clientSecret}' \
+
+  --data refresh_token=__AUTH0_REFRESH_TOKEN__
+
+
+ +

If the request was successful, your device application receives an HTTP 200 response with the following payload:

{
+
+  "access_token": "eyJ...MoQ",
+
+  "expires_in": 86400,
+
+  "scope": "openid offline_access",
+
+  "id_token": "eyJ...0NE",
+
+  "token_type": "Bearer"
+
+}
+
+
+ +

To learn more about refresh tokens, read Refresh Tokens.

+ +## Troubleshooting + + +

Tenant logs are created for any interaction that takes place and can be used to troubleshoot issues.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
**Code****Name****Description**
fdeazFailed device authorization request
fdeacFailed device activation
fdeccUser canceled the device confirmation
fedeFailed ExchangeDevice Code for Access Token
sedeSuccess ExchangeDevice Code for Access Token

Token responses

While you wait for the user to authorize the device, you may receive a few different HTTP 4xx responses.

Authorization pending

You will see this error while waiting for the user to take action. Continue polling using the suggested interval retrieved in the previous step of this tutorial.

`HTTP 403`
+
+
+
+{ 
+
+  "error": "authorization_pending", 
+
+  "error_description": "..." 
+
+}
+
+
+ +

Slow down

You are polling too fast. Slow down and use the suggested interval retrieved in the previous step of this tutorial. To avoid receiving this error due to network latency, you should start counting each interval after receipt of the last polling request's response.

`HTTP 429`
+
+
+
+{
+
+  "error": "slow_down",
+
+  "error_description": "..."
+
+}
+
+
+ +

Expired token

The user has not authorized the device quickly enough, so the device_code has expired. Your application should notify the user that the flow has expired and prompt them to reinitiate the flow.

The expired_token error is returned exactly once. After that, the endpoint returns the invalid_grant error.

`HTTP 403`
+
+
+
+{ 
+
+  "error": "expired_token",
+
+  "error_description": "..."
+
+}
+
+
+ +

Access Denied

If access is denied, you receive:

`HTTP 403`
+
+
+
+{
+
+  "error": "access_denied",
+
+  "error_description": "..."
+
+}
+
+
+ +

This can occur for a variety of reasons, including:

  • The user refused to authorize the device.

  • The authorization server denied the transaction.

  • A configured Action denied access.

+ +## Sample implementations + + +

Refer to the samples below to learn how to implement this flow in real-world applications.

  • Device Authorization Playground

  • AppleTV (Swift): Simple application that shows how Auth0 can be used with the Device Authorization Flow from an AppleTV.

  • CLI (Node.js): Sample implementation of a CLI that uses the Device Authorization Flow instead of the Authorization Code Flow. The major difference is that your CLI does not need to host a webserver and listen on a port.

+ +## Limitations + + +

To use the Device Authorization Flow, a device application must:

In addition, the Device Authorization Flow does not allow:

diff --git a/articles/quickstart/native/flutter/files/app/build.md b/articles/quickstart/native/flutter/files/app/build.md new file mode 100644 index 0000000000..c696fb8793 --- /dev/null +++ b/articles/quickstart/native/flutter/files/app/build.md @@ -0,0 +1,21 @@ +--- +name: app/build.gradle +language: +--- + +``` +apply plugin: 'com.android.application' + +android { + defaultConfig { + applicationId "com.auth0.samples" + minSdkVersion 21 + targetSdkVersion flutter.targetSdkVersion + // ... + + // ---> Add the next line + manifestPlaceholders += [auth0Domain: "auth0-dsepaid.auth0.com", auth0Scheme: "https"] + // <--- + } +} +``` diff --git a/articles/quickstart/native/flutter/files/main_view.md b/articles/quickstart/native/flutter/files/main_view.md new file mode 100644 index 0000000000..7ad3aa9879 --- /dev/null +++ b/articles/quickstart/native/flutter/files/main_view.md @@ -0,0 +1,68 @@ +--- +name: main_view.dart +language: dart +--- + +```dart +import 'package:auth0_flutter/auth0_flutter.dart'; +import 'package:flutter/material.dart'; +import 'profile_view.dart'; + +class MainView extends StatefulWidget { + const MainView({Key? key}) : super(key: key); + + @override + State createState() => _MainViewState(); +} + +class _MainViewState extends State { + Credentials? _credentials; + + late Auth0 auth0; + + @override + void initState() { + super.initState(); + auth0 = Auth0('auth0-dsepaid.auth0.com', 'OywWcFnHsbz50TfbRLWK0YTzrpxkwQL5'); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_credentials == null) + ElevatedButton( + onPressed: () async { + // Use a Universal Link callback URL on iOS 17.4+ / macOS 14.4+ + // useHTTPS is ignored on Android + final credentials = + await auth0.webAuthentication().login(useHTTPS: true); + + setState(() { + _credentials = credentials; + }); + }, + child: const Text("Log in")) + else + Column( + children: [ + ProfileView(user: _credentials!.user), + ElevatedButton( + onPressed: () async { + // Use a Universal Link logout URL on iOS 17.4+ / macOS 14.4+ + // useHTTPS is ignored on Android + await auth0.webAuthentication().logout(useHTTPS: true); + + setState(() { + _credentials = null; + }); + }, + child: const Text("Log out")) + ], + ) + ], + ); + } +} +``` diff --git a/articles/quickstart/native/flutter/files/profile_view.md b/articles/quickstart/native/flutter/files/profile_view.md new file mode 100644 index 0000000000..3dedf5c4b3 --- /dev/null +++ b/articles/quickstart/native/flutter/files/profile_view.md @@ -0,0 +1,26 @@ +--- +name: profile_view.dart +language: dart +--- + +```dart +import 'package:auth0_flutter/auth0_flutter.dart'; +import 'package:flutter/material.dart'; + +class ProfileView extends StatelessWidget { + const ProfileView({Key? key, required this.user}) : super(key: key); + + final UserProfile user; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (user.name != null) Text(user.name!), + if (user.email != null) Text(user.email!) + ], + ); + } +} +``` diff --git a/articles/quickstart/native/flutter/interactive.md b/articles/quickstart/native/flutter/interactive.md index 99d622f8f2..20cf5141a4 100644 --- a/articles/quickstart/native/flutter/interactive.md +++ b/articles/quickstart/native/flutter/interactive.md @@ -1,65 +1,41 @@ --- -title: Add login to your Flutter app -default: true -description: This tutorial demonstrates how to add user login with Auth0 to an Android, iOS, or macOS Flutter app using the Auth0 Flutter SDK -budicon: 448 -topics: - - quickstarts - - native - - flutter - - dart - - ios - - macos - - android -contentType: tutorial -useCase: quickstart -interactive: true +title: Add Login to Your Flutter Application +description: This guide demonstrates how to integrate Auth0 with a Flutter app using the Auth0 Flutter SDK. +interactive: true files: - - files/build - - files/main - - files/profile + - files/app/build + - files/main_view + - files/profile_view github: - path: sample + path: https://github.com/auth0-samples/auth0-flutter-samples/tree/main/sample +locale: en-US --- # Add Login to Your Flutter Application -Auth0 allows you to quickly add authentication and access user profile information in your app. This guide demonstrates how to integrate Auth0 with a Flutter app using the Auth0 Flutter SDK. -:::note -The Flutter SDK currently only supports Flutter apps for Android, iOS, and macOS. -::: - -This quickstart assumes you already have a Flutter app up and running. If not, check out the Flutter "getting started" guides to get started with a simple app. +

Auth0 allows you to quickly add authentication and access user profile information in your app. This guide demonstrates how to integrate Auth0 with a Flutter app using the Auth0 Flutter SDK.

The Flutter SDK currently only supports Flutter apps for Android, iOS, and macOS.

This quickstart assumes you already have a Flutter app up and running. If not, check out the Flutter "getting started" guides to get started with a simple app.

You should also be familiar with the Flutter command line tool.

New to Auth? Learn How Auth0 works, how it integrates with Single-Page Applications and which protocol it uses.

-You should also be familiar with the Flutter command line tool. +## Configure Auth0 -<%= include('_configure_urls_interactive') %> -## Install the Auth0 Flutter SDK +

To use Auth0 services, you need to have an application set up in the Auth0 Dashboard. The Auth0 application is where you will configure how you want authentication to work for your project.

Configure an Auth0 application

Use the interactive selector to create a new Auth0 application or select an existing Native Auth0 application. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code will use to call Auth0 APIs through the SDK.

Any settings you configure using this quickstart will automatically update for your application in the Dashboard, which is where you can manage your applications in the future.

If you would rather explore a complete configuration, you can view a sample app instead.

Configure the callback and logout URLs

The callback and logout URLs are the URLs that Auth0 invokes to redirect back to your app. Auth0 invokes the callback URL after authenticating the user, and the logout URL after removing the session cookie. If the callback and logout URLs are not set, users will be unable to log in and out of the app and will get an error.

Set the callback and logout URLs to the following values, depending on your platform.

On Android, the value of the SCHEME placeholder can be https or some other custom scheme. https schemes require enabling Android App Links.

On iOS 17.4+ and macOS 14.4+ it is possible to use Universal Links (https scheme) as callback and logout URLs. When enabled, the SDK will fall back to using a custom URL scheme on older iOS / macOS versions –your app's bundle identifier. This feature requires Xcode 15.3+ and a paid Apple Developer account.

Android

SCHEME://${account.namespace}/android/YOUR_PACKAGE_NAME/callback

iOS

https://${account.namespace}/ios/YOUR_BUNDLE_IDENTIFIER/callback, -Add the Auth0 Flutter SDK into the project: +YOUR_BUNDLE_IDENTIFIER://${account.namespace}/ios/YOUR_BUNDLE_IDENTIFIER/callback

macOS

https://${account.namespace}/macos/YOUR_BUNDLE_IDENTIFIER/callback, -```shell -flutter pub add auth0_flutter -``` +YOUR_BUNDLE_IDENTIFIER://${account.namespace}/macos/YOUR_BUNDLE_IDENTIFIER/callback

For example, if your iOS bundle identifier were com.example.MyApp and your Auth0 domain were example.us.auth0.com, then this value would be:

https://example.us.auth0.com/ios/com.example.MyApp/callback, -## Configure Android {{{ data-action=code data-code="app/build.gradle#11" }}} +com.example.MyApp://example.us.auth0.com/ios/com.example.MyApp/callback

-If you are not developing for the Android platform, skip this step. +## Install the Auth0 Flutter SDK -The SDK requires manifest placeholders. Auth0 uses placeholders internally to define an `intent-filter`, which captures the authentication callback URL. You must set the Auth0 tenant domain and the callback URL scheme. -The sample uses the following placeholders: +

Add the Auth0 Flutter SDK into the project:

flutter pub add auth0_flutter

-- `auth0Domain`: The domain of your Auth0 tenant. Generally, you find this in the Auth0 Dashboard under your **Application Settings** in the Domain field. If you are using a custom domain, you should set this to the value of your custom domain instead. -- `auth0Scheme`: The scheme to use. Can be a custom scheme, or `https` if you want to use Android App Links. You can read more about setting this value in the Auth0.Android SDK README. +## Configure Android -:::note -You do not need to declare a specific `intent-filter` for your activity because you defined the manifest placeholders with your Auth0 **Domain** and **Scheme** values. The library handles the redirection for you. -::: -Run **Sync Project with Gradle Files** inside Android Studio to apply your changes. +

If you are not developing for the Android platform, skip this step.

The SDK requires manifest placeholders. Auth0 uses placeholders internally to define an intent-filter, which captures the authentication callback URL. You must set the Auth0 tenant domain and the callback URL scheme.

The sample uses the following placeholders:

  • auth0Domain: The domain of your Auth0 tenant. Generally, you find this in the Auth0 Dashboard under your Application Settings in the Domain field. If you are using a custom domain, you should set this to the value of your custom domain instead.

  • auth0Scheme: The scheme to use. Can be a custom scheme, or https if you want to use Android App Links. You can read more about setting this value in the Auth0.Android SDK README.

You do not need to declare a specific intent-filter for your activity because you defined the manifest placeholders with your Auth0 Domain and Scheme values. The library handles the redirection for you.

Run Sync Project with Gradle Files inside Android Studio to apply your changes.

## Configure iOS/macOS @@ -97,75 +73,33 @@ For the associated domain to work, your app must be signed with your team certif ## Add login to your application {{{ data-action="code" data-code="main_view.dart#29:40" }}} -Universal Login is the easiest way to set up authentication in your app. We recommend using it for the best experience, best security, and the fullest array of features. -Integrate Auth0 Universal Login in your Flutter app by using the `Auth0` class. Redirect your users to the Auth0 Universal Login page using `webAuthentication().login()`. This is a `Future` and must be awaited for you to retrieve the user's tokens. +

Universal Login is the easiest way to set up authentication in your app. We recommend using it for the best experience, best security, and the fullest array of features.

Integrate Auth0 Universal Login in your Flutter app by using the Auth0 class. Redirect your users to the Auth0 Universal Login page using webAuthentication().login(). This is a Future and must be awaited for you to retrieve the user's tokens.

Android: if you are using a custom scheme, pass this scheme to the login method so that the SDK can route to the login page and back again correctly:

await auth0.webAuthentication(scheme: 'YOUR CUSTOM SCHEME').login();

When a user logs in, they are redirected back to your app. Then, you are able to access the ID and access tokens for this user.

Flutter - Step 5 - Add login to your application

Add a button to your app that calls webAuthentication().login() and logs the user into your app. Verify that you are redirected to Auth0 for authentication and then back to your app.

Verify that you can get access to the tokens on the result of calling login.

-**Android**: if you are using a custom scheme, pass this scheme to the login method so that the SDK can route to the login page and back again correctly: +
-```dart -await auth0.webAuthentication(scheme: 'YOUR CUSTOM SCHEME').login(); -``` - -When a user logs in, they are redirected back to your app. Then, you are able to access the ID and access tokens for this user. - -::::checkpoint -:::checkpoint-default -Add a button to your app that calls `webAuthentication().login()` and logs the user into your app. Verify that you are redirected to Auth0 for authentication and then back to your app. +

If your app did not launch successfully:

  • Ensure you set the Allowed Callback URLs are correct

  • Verify you saved your changes after entering your URLs

  • Make sure the domain and client ID values are imported correctly

  • If using Android, ensure that the manifest placeholders have been set up correctly, otherwise the redirect back to your app may not work

Still having issues? Check out our documentation or visit our community page to get more help.

-Verify that you can get access to the tokens on the result of calling `login`. -::: +

-:::checkpoint-failure -If your app did not launch successfully: +## Add logout to your application {{{ data-action="code" data-code="main_view.dart#45:55" }}} -- Ensure you set the Allowed Callback URLs are correct -- Verify you saved your changes after entering your URLs -- Make sure the domain and client ID values are imported correctly -- If using Android, ensure that the manifest placeholders have been set up correctly, otherwise the redirect back to your app may not work -Still having issues? Check out our documentation or visit our community page to get more help. -::: -:::: +

To log users out, redirect them to the Auth0 logout endpoint to clear their login session by calling the Auth0 Flutter SDK webAuthentication().logout(). Read more about logging out of Auth0.

Android: if you are using a custom scheme, pass this scheme to the logout method so that the SDK can route back to your app correctly:

await auth0.webAuthentication(scheme: 'YOUR CUSTOM SCHEME').logout();

Flutter - Step 6 - Add logout to your application

Add a button to your app that calls webAuthentication().logout() and logs the user out of your app. When you select it, verify that your Flutter app redirects you to the logout endpoint and back again. You should not be logged in to your app.

-## Add logout to your application {{{ data-action=code data-code="main_view.dart#45:55"}}} +
-To log users out, redirect them to the Auth0 logout endpoint to clear their login session by calling the Auth0 Flutter SDK `webAuthentication().logout()`. Read more about logging out of Auth0. +

If your app did not log out successfully:

  • Ensure the Allowed Logout URLs are set properly

  • Verify you saved your changes after entering your URLs

Still having issues? Check out our documentation or visit our community page to get more help.

-**Android**: if you are using a custom scheme, pass this scheme to the logout method so that the SDK can route back to your app correctly: - -``` -await auth0.webAuthentication(scheme: 'YOUR CUSTOM SCHEME').logout(); -``` - -::::checkpoint -:::checkpoint-default -Add a button to your app that calls `webAuthentication().logout()` and logs the user out of your app. When you select it, verify that your Flutter app redirects you to the logout endpoint and back again. You should not be logged in to your app. -::: - -:::checkpoint-failure -If your app did not log out successfully: - -- Ensure the Allowed Logout URLs are set properly -- Verify you saved your changes after entering your URLs - -Still having issues? Check out our documentation or visit our community page to get more help. -::: -:::: +

## Show user profile information {{{ data-action="code" data-code="profile_view.dart" }}} -The user profile automatically retrieves user profile properties for you when you call `webAuthentication().login()`. The returned object from the login step contains a `user` property with all the user profile properties, which populates by decoding the ID token. -::::checkpoint -:::checkpoint-default -Log in and inspect the `user` property on the result. Verify the current user's profile information, such as `email` or `name`. -::: -:::checkpoint-failure -If your app did not return user profile information: +

The user profile automatically retrieves user profile properties for you when you call webAuthentication().login(). The returned object from the login step contains a user property with all the user profile properties, which populates by decoding the ID token.

Flutter - Step 7 - Show user profile information

Log in and inspect the user property on the result. Verify the current user's profile information, such as email or name.

-- Verify the access token is valid +
-Still having issues? Check out our documentation or visit our community page to get more help. -::: -:::: +

If your app did not return user profile information:

  • Verify the access token is valid

Still having issues? Check out our documentation or visit our community page to get more help.

+ +

diff --git a/articles/quickstart/native/ionic-angular/files/app.component.md b/articles/quickstart/native/ionic-angular/files/app.component.md new file mode 100644 index 0000000000..0b54654bab --- /dev/null +++ b/articles/quickstart/native/ionic-angular/files/app.component.md @@ -0,0 +1,49 @@ +--- +name: app.component.ts +language: javascript +--- + +```javascript +import { Component, OnInit, NgZone } from '@angular/core'; +import { AuthService } from '@auth0/auth0-angular'; +import { mergeMap } from 'rxjs/operators'; +import { Browser } from '@capacitor/browser'; +import { App } from '@capacitor/app'; + +const callbackUri = `<%= "${config.appId}" %>://${account.namespace}/capacitor/<%= "${config.appId}" %>/callback`; + +@Component({ + selector: 'app-root', + templateUrl: 'app.component.html', + styleUrls: ['app.component.scss'], +}) +export class AppComponent implements OnInit { + // Import the AuthService module from the Auth0 Angular SDK + constructor(public auth: AuthService, private ngZone: NgZone) {} + + ngOnInit(): void { + // Use Capacitor's App plugin to subscribe to the `appUrlOpen` event + App.addListener('appUrlOpen', ({ url }) => { + // Must run inside an NgZone for Angular to pick up the changes + // https://capacitorjs.com/docs/guides/angular + ngZone.run(() => { + if (url?.startsWith(callbackUri)) { + // If the URL is an authentication callback URL.. + if ( + url.includes('state=') && + (url.includes('error=') || url.includes('code=')) + ) { + // Call handleRedirectCallback and close the browser + this.auth + .handleRedirectCallback(url) + .pipe(mergeMap(() => Browser.close())) + .subscribe(); + } else { + Browser.close(); + } + } + }); + }); + } +} +``` diff --git a/articles/quickstart/native/ionic-angular/files/app.module.md b/articles/quickstart/native/ionic-angular/files/app.module.md new file mode 100644 index 0000000000..987cc68c0d --- /dev/null +++ b/articles/quickstart/native/ionic-angular/files/app.module.md @@ -0,0 +1,37 @@ +--- +name: app.module.ts +language: javascript +--- + +```javascript +// Import the types from the SDK +import { AuthModule } from '@auth0/auth0-angular'; +import config from '../../capacitor.config'; + +// .. + +// Build the URL that Auth0 should redirect back to +const redirect_uri = `<%= "${config.appId}" %>://${account.namespace}/capacitor/<%= "${config.appId}" %>/callback`; + +// Register AuthModule with your AppModule +@NgModule({ + declarations: [AppComponent], + entryComponents: [], + imports: [ + BrowserModule, + IonicModule.forRoot(), + AppRoutingModule, + AuthModule.forRoot({ + domain: "${account.namespace}", + clientId: "${account.clientId}", + useRefreshTokens: true, + useRefreshTokensFallback: false, + authorizationParams: { + redirect_uri + } + }), + ], + providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }], + bootstrap: [AppComponent], +}) +``` diff --git a/articles/quickstart/native/ionic-angular/files/login-button.md b/articles/quickstart/native/ionic-angular/files/login-button.md index dc3616f928..7821093b94 100644 --- a/articles/quickstart/native/ionic-angular/files/login-button.md +++ b/articles/quickstart/native/ionic-angular/files/login-button.md @@ -2,7 +2,7 @@ name: login-button.ts language: javascript --- - + ```javascript import { Component, OnInit } from '@angular/core'; import { AuthService } from '@auth0/auth0-angular'; @@ -26,4 +26,4 @@ export class LoginButtonComponent { .subscribe(); } } -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-angular/files/logout-button.md b/articles/quickstart/native/ionic-angular/files/logout-button.md index 21ad6aad68..32da066dac 100644 --- a/articles/quickstart/native/ionic-angular/files/logout-button.md +++ b/articles/quickstart/native/ionic-angular/files/logout-button.md @@ -2,7 +2,7 @@ name: logout-button.ts language: javascript --- - + ```javascript import { Component, OnInit } from '@angular/core'; import { AuthService } from '@auth0/auth0-angular'; @@ -33,4 +33,4 @@ export class LogoutButtonComponent { .subscribe(); } } -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-angular/files/user-profile.md b/articles/quickstart/native/ionic-angular/files/user-profile.md index 96be742cdd..01fea0f83d 100644 --- a/articles/quickstart/native/ionic-angular/files/user-profile.md +++ b/articles/quickstart/native/ionic-angular/files/user-profile.md @@ -2,7 +2,7 @@ name: user-profile.ts language: javascript --- - + ```javascript import { Component, OnInit } from '@angular/core'; import { AuthService } from '@auth0/auth0-angular'; @@ -21,4 +21,4 @@ import { AuthService } from '@auth0/auth0-angular'; export class ProfileComponent { constructor(public auth: AuthService) {} } -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-angular/interactive.md b/articles/quickstart/native/ionic-angular/interactive.md index 463c683893..9528c94eb4 100644 --- a/articles/quickstart/native/ionic-angular/interactive.md +++ b/articles/quickstart/native/ionic-angular/interactive.md @@ -1,40 +1,39 @@ --- -title: Add login to your Ionic Angular with Capacitor app -default: true -description: This tutorial demonstrates how to add user login with Auth0 to an Ionic Angular & Capacitor application. -budicon: 448 -topics: - - quickstarts - - native - - ionic - - angular - - capacitor -github: - path: angular -contentType: tutorial -useCase: quickstart -interactive: true +title: Add Login to Your Ionic Angular with Capacitor Application +description: This guide demonstrates how to integrate Auth0 with an Ionic (Angular) & Capacitor application using the Auth0 Angular SDK. +interactive: true files: - - files/app-module - - files/app-component - - files/login-button - - files/logout-button - - files/user-profile + - files/app.module + - files/app.component + - files/login-button + - files/logout-button + - files/user-profile +github: + path: https://github.com/auth0-samples/auth0-ionic-samples/tree/main/angular +locale: en-US --- # Add Login to Your Ionic Angular with Capacitor Application -Auth0 allows you to quickly add authentication and access user profile information in your application. This guide demonstrates how to integrate Auth0 with an Ionic (Angular) & Capacitor application using the Auth0 Angular SDK. -<%= include('../_includes/ionic/_article_intro') %> +

Auth0 allows you to quickly add authentication and access user profile information in your application. This guide demonstrates how to integrate Auth0 with an Ionic (Angular) & Capacitor application using the Auth0 Angular SDK.

+ +## Getting started + + +

This quickstart assumes you already have an Ionic application up and running with Capacitor. If not, check out the Using Capacitor with Ionic Framework guide to get started with a simple app, or clone our sample apps.

You should also be familiar with the Capacitor development workflow.

-<%= include('../_includes/ionic/_configure_urls_interactive') %> +## Configure Auth0 -<%= include('../../_includes/_auth0-angular-install.md') %> -<%= include('../_includes/ionic/_install_plugins') %> +

To use Auth0 services, you need to have an application set up in the Auth0 Dashboard. The Auth0 application is where you will configure how you want authentication to work for your project.

Throughout this article, YOUR_PACKAGE_ID is your application's package ID. This can be found and configured in the appId field in your capacitor.config.ts file. See Capacitor's Config schema for more info.

Configure an application

Use the interactive selector to create a new Auth0 application or select an existing application that represents the project you want to integrate with. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code will use to call Auth0 APIs through the SDK.

Any settings you configure using this quickstart will automatically update for your application in the Dashboard, which is where you can manage your applications in the future.

If you would rather explore a complete configuration, you can view a sample application instead.

Configure Callback URLs

A callback URL is a URL in your application that you would like Auth0 to redirect users to after they have authenticated. If not set, users will not be returned to your application after they log in.

If you are following along with our sample project, set this to:

YOUR_PACKAGE_ID://{yourDomain}/capacitor/YOUR_PACKAGE_ID/callback

Configure Logout URLs

A logout URL is a URL in your application that you would like Auth0 to redirect users to after they have logged out. If not set, users will not be able to log out from your application and will receive an error.

If you are following along with our sample project, set this to:

YOUR_PACKAGE_ID://{yourDomain}/capacitor/YOUR_PACKAGE_ID/callback

Configure Allowed Origins

To be able to make requests from your native application to Auth0, set the following Allowed Origins in your Application Settings.

If you are following along with our sample project, set this to capacitor://localhost, http://localhost for iOS and Android respectively.

Lastly, be sure that the Application Type for your application is set to Native in the Application Settings.

-## Register and configure the authentication module {{{ data-action=code data-code="app.module.ts" }}} +## Install the Auth0 Angular SDK + + +

Run the following command within your project directory to install the Auth0 Angular SDK:

npm install @auth0/auth0-angular

The SDK exposes several types that help you integrate Auth0 with your Angular application idiomatically, including a module and an authentication service.

Install Capacitor plugins

This quickstart and sample make use of some of Capacitor's official plugins. Install these into your app using the following command:

npm install @capacitor/browser @capacitor/app

  • @capacitor/browser: Allows you to interact with the device's system browser and is used to open the URL to Auth0's authorization endpoint.

  • @capacitor/app: Allows you to subscribe to high-level app events, useful for handling callbacks from Auth0.

Capacitor's Browser plugin on iOS uses SFSafariViewController, which on iOS 11+ does not share cookies with Safari on the device. This means that SSO will not work on those devices. If you need SSO, please instead use a compatible plugin that uses ASWebAuthenticationSession.

+ +## Register and configure the authentication module {{{ data-action="code" data-code="app.module.ts" }}} The SDK exports `AuthModule`, a module that contains all the services required for the SDK to function. This module should be registered with your application and be configured with your Auth0 domain and Client ID. @@ -62,74 +61,46 @@ Still having issues? Check out our -::::checkpoint -::: checkpoint-default -The `loginWithRedirect` function tells the SDK to initiate the login flow, using the `Browser.open` function to open the login URL with the platform's system browser component by setting the `openUrl` parameter. This provides a way for your user to log in to your application. Users redirect to the login page at Auth0 and do not receive any errors. -::: +

In a Capacitor application, the Capacitor's Browser plugin performs a redirect to the Auth0 Universal Login Page. Set the openUrl parameter on the loginWithRedirect function to use Browser.open so that the URL is opened using the device's system browser component (SFSafariViewController on iOS, and Chrome Custom Tabs on Android).

By default, the SDK's loginWithRedirect method uses window.location.href to navigate to the login page in the default browser application on the user's device rather than the system browser component appropriate for the platform. The user would leave your application to authenticate and could make for a suboptimal user experience.

Ionic & Capacitor (Angular) - Step 5 - Add login to your application

The loginWithRedirect function tells the SDK to initiate the login flow, using the Browser.open function to open the login URL with the platform's system browser component by setting the openUrl parameter. This provides a way for your user to log in to your application. Users redirect to the login page at Auth0 and do not receive any errors.

-::: checkpoint-failure -Sorry about that. Here's a couple things to double check: +
-* ensure that there are no errors in the browser's console window at the point of login -* ensure the domain and Client ID are correct according to your Auth0 application in the Dashboard -* if you are redirected to Auth0 and receive an error page, check the "technical details" section at the bottom for the reason for the failure -::: -:::: +

Sorry about that. Here's a couple things to double check:

  • ensure that there are no errors in the browser's console window at the point of login

  • ensure the domain and Client ID are correct according to your Auth0 application in the Dashboard

  • if you are redirected to Auth0 and receive an error page, check the "technical details" section at the bottom for the reason for the failure

-## Handling the login callback {{{ data-action=code data-code="app.component.ts" }}} +

-<%= include('../_includes/ionic/_handle_callback_intro') %> +## Handling the login callback {{{ data-action="code" data-code="app.component.ts" }}} -Note that the `appUrlOpen` event callback is wrapped in `ngZone.run`. Changes to observables that occur when `handleRedirectCallback` runs are picked up by the Angular app. To learn more, read Using Angular with Capacitor. Otherwise, the screen doesn't update to show the authenticated state after log in. -<%= include('../_includes/ionic/_note_custom_schemes') %> +

Once users logs in with the Universal Login Page, they redirect back to your app via a URL with a custom URL scheme. The appUrlOpen event must be handled within your app. You can call the handleRedirectCallback method from the Auth0 SDK to initialize the authentication state.

You can only use this method on a redirect from Auth0. To verify success, check for the presence of the code and state parameters in the URL.

The Browser.close() method should close the browser when this event is raised.

Note that the appUrlOpen event callback is wrapped in ngZone.run. Changes to observables that occur when handleRedirectCallback runs are picked up by the Angular app. To learn more, read Using Angular with Capacitor. Otherwise, the screen doesn't update to show the authenticated state after log in.

This article assumes you will be using Custom URL Schemes to handle the callback within your application. To do this, register your YOUR_PACKAGE_ID as a URL scheme for your chosen platform. To learn more, read Defining a Custom URL Scheme for iOS, or Create Deep Links to App Content for Android.

Ionic & Capacitor (Angular) - Step 6 - Handling the login callback

Add the appUrlOpen to your application's App component and log in. The browser window should close once the user authenticates and logs in to your app.

-::::checkpoint -:::checkpoint-default -Add the `appUrlOpen` to your application's `App` component and log in. The browser window should close once the user authenticates and logs in to your app. -::: +
-:::checkpoint-failure -Sorry about that. Here's a couple things to double check: +

Sorry about that. Here's a couple things to double check:

-* check that the custom URL scheme is registered for your chosen platform. On iOS, define a custom URL scheme, or add an intent filter with your custom scheme for Android -* if the event fires but you receive an error, check the logs in your Auth0 Dashboard for the error code -::: -:::: +

-## Add logout to your application {{{ data-action=code data-code="logout-button.ts" }}} +## Add logout to your application {{{ data-action="code" data-code="logout-button.ts" }}} -<%= include('../_includes/ionic/_add_logout_intro.md') %> -::::checkpoint -:::checkpoint-default -Provide a way for your users to log out of your application. Verify the redirect to Auth0 and then to the address you specified in the `returnTo` parameter. Make sure users are no longer logged in to your application. -::: +

Now that users can log in, you need to configure a way to log out. Users must redirect to the Auth0 logout endpoint in the browser to clear their browser session. Again, Capacitor's Browser plugin should perform this redirect so that the user does not leave your app and receive a suboptimal experience.

To achieve this with Ionic and Capacitor in conjunction with the Auth0 SDK:

  • Construct the URL for your app Auth0 should use to redirect to after logout. This is a URL that uses your registered custom scheme and Auth0 domain. Add it to your Allowed Logout URLs configuration in the Auth0 Dashboard

  • Logout from the SDK by calling logout, and pass your redirect URL back as the logoutParams.returnTo parameter.

  • Set the openUrl parameter to a callback that uses the Capacitor browser plugin to open the URL using Browser.open.

Similar to the login step, if you do not set openUrl when calling logout, the SDK redirects the user to the logout URL using the default browser application on the device, which provides a suboptimal user experience.

Ionic & Capacitor (Angular) - Step 7 - Add logout to your application

Provide a way for your users to log out of your application. Verify the redirect to Auth0 and then to the address you specified in the returnTo parameter. Make sure users are no longer logged in to your application.

-:::checkpoint-failure -Sorry about that. Here's a couple things to double check: +
-* check that the URL you provided to in the `returnTo` parameter is registered as an allowed callback URL in your Auth0 Dashboard -::: -:::: +

Sorry about that. Here's a couple things to double check:

  • check that the URL you provided to in the returnTo parameter is registered as an allowed callback URL in your Auth0 Dashboard

-## Show the user profile {{{ data-action=code data-code="user-profile.ts" }}} +

-The Auth0 SDK retrieves the profile information associated with logged-in users in whatever component you need, such as their name or profile picture, to personalize the user interface. The profile information is available through the `user$` property exposed by `AuthService`. +## Show the user profile {{{ data-action="code" data-code="user-profile.ts" }}} -::::checkpoint -:::checkpoint-default -Provide a way for your users to see their user profile details within the app and verify you are able to retrieve and see your profile information on screen once you have logged in. -::: -:::checkpoint-failure -Sorry about that. Here's a couple things to double check: +

The Auth0 SDK retrieves the profile information associated with logged-in users in whatever component you need, such as their name or profile picture, to personalize the user interface. The profile information is available through the user$ property exposed by AuthService.

Ionic & Capacitor (Angular) - Step 8 - Show the user profile

Provide a way for your users to see their user profile details within the app and verify you are able to retrieve and see your profile information on screen once you have logged in.

-* check that you are only reading the user's profile when `isLoading` is `false` -* check that `user` resolves to an object and is not `undefined` -::: -:::: +
+ +

Sorry about that. Here's a couple things to double check:

  • check that you are only reading the user's profile when isLoading is false

  • check that user resolves to an object and is not undefined

+ +

diff --git a/articles/quickstart/native/ionic-react/files/app.md b/articles/quickstart/native/ionic-react/files/app.md index 4933e1a6de..f41566cdb1 100644 --- a/articles/quickstart/native/ionic-react/files/app.md +++ b/articles/quickstart/native/ionic-react/files/app.md @@ -2,7 +2,7 @@ name: app.tsx language: javascript --- - + ```javascript // Import Capacitor's app and browser plugins, giving us access to `addListener` and `appUrlOpen`, // as well as the bits needed for Auth0 and React @@ -13,6 +13,31 @@ import { useAuth0 } from '@auth0/auth0-react'; // ... +const App: React.FC = () => { + // Get the callback handler from the Auth0 React hook + const { handleRedirectCallback } = useAuth0(); + + useEffect(() => { + // Handle the 'appUrlOpen' event and call `handleRedirectCallback` + CapApp.addListener('appUrlOpen', async ({ url }) => { + if (url.includes('state') && (url.includes('code') || url.includes('error'))) { + await handleRedirectCallback(url); + } + // No-op on Android + await Browser.close(); + }); + }, [handleRedirectCallback]); + + // .. +};// Import Capacitor's app and browser plugins, giving us access to `addListener` and `appUrlOpen`, +// as well as the bits needed for Auth0 and React +import { App as CapApp } from '@capacitor/app'; +import { Browser } from '@capacitor/browser'; +import { useEffect } from 'react'; +import { useAuth0 } from '@auth0/auth0-react'; + +// ... + const App: React.FC = () => { // Get the callback handler from the Auth0 React hook const { handleRedirectCallback } = useAuth0(); @@ -30,4 +55,4 @@ const App: React.FC = () => { // .. }; -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-react/files/index.md b/articles/quickstart/native/ionic-react/files/index.md index 762a3d2f70..ab848413f9 100644 --- a/articles/quickstart/native/ionic-react/files/index.md +++ b/articles/quickstart/native/ionic-react/files/index.md @@ -2,7 +2,7 @@ name: index.tsx language: javascript --- - + ```javascript import React from 'react'; import { createRoot } from 'react-dom/client'; @@ -24,4 +24,4 @@ root.render( ); -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-react/files/login-button.md b/articles/quickstart/native/ionic-react/files/login-button.md index 563b1ee5af..20dc0072e2 100644 --- a/articles/quickstart/native/ionic-react/files/login-button.md +++ b/articles/quickstart/native/ionic-react/files/login-button.md @@ -2,7 +2,7 @@ name: login-button.tsx language: javascript --- - + ```javascript import { useAuth0 } from '@auth0/auth0-react'; import { Browser } from '@capacitor/browser'; @@ -27,4 +27,4 @@ const LoginButton: React.FC = () => { }; export default LoginButton; -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-react/files/logout-button.md b/articles/quickstart/native/ionic-react/files/logout-button.md index a4a1ceb4f1..87d163ec18 100644 --- a/articles/quickstart/native/ionic-react/files/logout-button.md +++ b/articles/quickstart/native/ionic-react/files/logout-button.md @@ -2,7 +2,7 @@ name: logout-button.tsx language: javascript --- - + ```javascript import { useAuth0 } from '@auth0/auth0-react'; import { Browser } from '@capacitor/browser'; @@ -34,4 +34,4 @@ const LogoutButton: React.FC = () => { }; export default LogoutButton; -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-react/files/user-profile.md b/articles/quickstart/native/ionic-react/files/user-profile.md index d3d719713b..8d811b0020 100644 --- a/articles/quickstart/native/ionic-react/files/user-profile.md +++ b/articles/quickstart/native/ionic-react/files/user-profile.md @@ -2,7 +2,7 @@ name: user-profile.tsx language: javascript --- - + ```javascript import { useAuth0 } from '@auth0/auth0-react'; @@ -22,4 +22,4 @@ const Profile: React.FC = () => { }; export default Profile; -``` \ No newline at end of file +``` diff --git a/articles/quickstart/native/ionic-react/interactive.md b/articles/quickstart/native/ionic-react/interactive.md index a92859a39b..28cd985773 100644 --- a/articles/quickstart/native/ionic-react/interactive.md +++ b/articles/quickstart/native/ionic-react/interactive.md @@ -1,40 +1,39 @@ --- title: Add login to your Ionic React with Capacitor app -default: true -description: This tutorial demonstrates how to add user login with Auth0 to an Ionic React & Capacitor application. -budicon: 448 -topics: - - quickstarts - - native - - ionic - - react - - capacitor -github: - path: react -contentType: tutorial -useCase: quickstart -interactive: true +description: This guide demonstrates how to integrate Auth0 with an Ionic (React) & Capacitor application using the Auth0 React SDK. +interactive: true files: - - files/index - - files/login-button - - files/logout-button - - files/app - - files/user-profile + - files/index + - files/login-button + - files/logout-button + - files/app + - files/user-profile +github: + path: https://github.com/auth0-samples/auth0-ionic-samples/tree/main/react +locale: en-US --- # Add login to your Ionic React with Capacitor app -Auth0 allows you to quickly add authentication and gain access to user profile information in your application. This guide demonstrates how to integrate Auth0 with an Ionic (React) & Capacitor application using the Auth0 React SDK. -<%= include('../_includes/ionic/_article_intro') %> +

Auth0 allows you to quickly add authentication and gain access to user profile information in your application. This guide demonstrates how to integrate Auth0 with an Ionic (React) & Capacitor application using the Auth0 React SDK.

+ +## Getting started + + +

This quickstart assumes you already have an Ionic application up and running with Capacitor. If not, check out the Using Capacitor with Ionic Framework guide to get started with a simple app, or clone our sample apps.

You should also be familiar with the Capacitor development workflow.

+ +## Configure Auth0 -<%= include('../_includes/ionic/_configure_urls_interactive') %> -<%= include('../../_includes/_auth0-react-install.md') %> +

To use Auth0 services, you need to have an application set up in the Auth0 Dashboard. The Auth0 application is where you will configure how you want authentication to work for your project.

Throughout this article, YOUR_PACKAGE_ID is your application's package ID. This can be found and configured in the appId field in your capacitor.config.ts file. See Capacitor's Config schema for more info.

Configure an application

Use the interactive selector to create a new Auth0 application or select an existing application that represents the project you want to integrate with. Every application in Auth0 is assigned an alphanumeric, unique client ID that your application code will use to call Auth0 APIs through the SDK.

Any settings you configure using this quickstart will automatically update for your application in the Dashboard, which is where you can manage your applications in the future.

If you would rather explore a complete configuration, you can view a sample application instead.

Configure Callback URLs

A callback URL is a URL in your application that you would like Auth0 to redirect users to after they have authenticated. If not set, users will not be returned to your application after they log in.

If you are following along with our sample project, set this to:

YOUR_PACKAGE_ID://{yourTenant}.auth0.com/capacitor/YOUR_PACKAGE_ID/callback

Configure Logout URLs

A logout URL is a URL in your application that you would like Auth0 to redirect users to after they have logged out. If not set, users will not be able to log out from your application and will receive an error.

If you are following along with our sample project, set this to:

YOUR_PACKAGE_ID://{yourTenant}.auth0.com/capacitor/YOUR_PACKAGE_ID/callback.

Configure Allowed Origins

To be able to make requests from your native application to Auth0, set the following Allowed Origins in your Application Settings.

If you are following along with our sample project, set this to capacitor://localhost, http://localhost for iOS and Android respectively.

Lastly, be sure that the Application Type for your application is set to Native in the Application Settings.

-<%= include('../_includes/ionic/_install_plugins') %> +## Install the Auth0 React SDK -## Configure the `Auth0Provider` component {{{ data-action=code data-code="index.tsx" }}} + +

Run the following command within your project directory to install the Auth0 React SDK:

npm install @auth0/auth0-react

The SDK exposes methods and variables that help you integrate Auth0 with your React application idiomatically using React Hooks or Higher-Order Components.

Install Capacitor plugins

This quickstart and sample make use of some of Capacitor's official plugins. Install these into your app using the following command:

npm install @capacitor/browser @capacitor/app

  • @capacitor/browser: Allows you to interact with the device's system browser and is used to open the URL to Auth0's authorization endpoint.

  • @capacitor/app: Allows you to subscribe to high-level app events, useful for handling callbacks from Auth0.

Capacitor's Browser plugin on iOS uses SFSafariViewController, which on iOS 11+ does not share cookies with Safari on the device. This means that SSO will not work on those devices. If you need SSO, please instead use a compatible plugin that uses ASWebAuthenticationSession.

+ +## Configure the Auth0Provider component {{{ data-action="code" data-code="index.tsx" }}} Under the hood, the Auth0 React SDK uses React Context to manage the authentication state of your users. One way to integrate Auth0 with your React app is to wrap your root component with an `Auth0Provider` you can import from the SDK. @@ -64,74 +63,46 @@ Still having issues? Check out our -::::checkpoint -::: checkpoint-default -The `loginWithRedirect` function tells the SDK to initiate the login flow, using the `Browser.open` function to open the login URL with the platform's system browser component by setting the `openUrl` parameter. This provides a way for your user to log in to your application. Users redirect to the login page at Auth0 and do not receive any errors. -::: +

In a Capacitor application, the Capacitor's Browser plugin performs a redirect to the Auth0 Universal Login Page. Set the openUrl parameter on the loginWithRedirect function to use Browser.open so that the URL is opened using the device's system browser component (SFSafariViewController on iOS, and Chrome Custom Tabs on Android).

By default, the SDK's loginWithRedirect method uses window.location.href to navigate to the login page in the default browser application on the user's device rather than the system browser component appropriate for the platform. The user would leave your application to authenticate and could make for a suboptimal user experience.

Ionic & Capacitor (React) - Step 5- Add login to your application

The loginWithRedirect function tells the SDK to initiate the login flow, using the Browser.open function to open the login URL with the platform's system browser component by setting the openUrl parameter. This provides a way for your user to log in to your application. Users redirect to the login page at Auth0 and do not receive any errors.

-::: checkpoint-failure -Sorry about that. Here's a couple things to double check: +
-- ensure that there are no errors in the browser's console window at the point of login -- ensure the domain and Client ID are correct according to your Auth0 application in the dashboard -- if you are redirected to Auth0 and receive an error page, check the "technical details" section at the bottom for the reason for the failure - ::: - :::: +

Sorry about that. Here's a couple things to double check:

  • ensure that there are no errors in the browser's console window at the point of login

  • ensure the domain and Client ID are correct according to your Auth0 application in the dashboard

  • if you are redirected to Auth0 and receive an error page, check the "technical details" section at the bottom for the reason for the failure

-## Handle the login callback {{{ data-action=code data-code="app.tsx" }}} +

-<%= include('../_includes/ionic/_handle_callback_intro') %> +## Handle the login callback {{{ data-action="code" data-code="app.tsx" }}} -<%= include('../_includes/ionic/_note_custom_schemes') %> -::::checkpoint -:::checkpoint-default -Add the `appUrlOpen` to your application's `App` component and log in. The browser window should close once the user authenticates and logs in to your app. -::: +

Once users logs in with the Universal Login Page, they redirect back to your app via a URL with a custom URL scheme. The appUrlOpen event must be handled within your app. You can call the handleRedirectCallback method from the Auth0 SDK to initialize the authentication state.

You can only use this method on a redirect from Auth0. To verify success, check for the presence of the code and state parameters in the URL.

The Browser.close() method should close the browser when this event is raised.

This article assumes you will be using Custom URL Schemes to handle the callback within your application. To do this, register your YOUR_PACKAGE_ID as a URL scheme for your chosen platform. To learn more, read Defining a Custom URL Scheme for iOS, or Create Deep Links to App Content for Android.

Ionic & Capacitor (React) - Step 6 - Handle the login callback

Add the appUrlOpen to your application's App component and log in. The browser window should close once the user authenticates and logs in to your app.

-:::checkpoint-failure -Sorry about that. Here's a couple things to double check: +
-- check that the custom URL scheme is registered for your chosen platform. On iOS, define a custom URL scheme, or add an intent filter with your custom scheme on Android -- if the event fires but you receive an error, check the logs in your Auth0 Dashboard for the reason for the error - ::: - :::: +

Sorry about that. Here's a couple things to double check:

-## Add logout to your application {{{ data-action=code data-code="logout-button.tsx" }}} +

-<%= include('../_includes/ionic/_add_logout_intro.md') %> +## Add logout to your application {{{ data-action="code" data-code="logout-button.tsx" }}} -::::checkpoint -:::checkpoint-default -Provide a way for your users to log out of your application. Verify that you redirect to Auth0 and then to the address you specified in the `returnTo` parameter. Check that you are no longer logged in to your application. -::: -:::checkpoint-failure -Sorry about that. Here's a couple things to double check: +

Now that users can log in, you need to configure a way to log out. Users must redirect to the Auth0 logout endpoint in the browser to clear their browser session. Again, Capacitor's Browser plugin should perform this redirect so that the user does not leave your app and receive a suboptimal experience.

To achieve this with Ionic and Capacitor in conjunction with the Auth0 SDK:

  • Construct the URL for your app Auth0 should use to redirect to after logout. This is a URL that uses your registered custom scheme and Auth0 domain. Add it to your Allowed Logout URLs configuration in the Auth0 Dashboard

  • Logout from the SDK by calling logout, and pass your redirect URL back as the logoutParams.returnTo parameter.

  • Set the openUrl parameter to a callback that uses the Capacitor browser plugin to open the URL using Browser.open.

Similar to the login step, if you do not set openUrl when calling logout, the SDK redirects the user to the logout URL using the default browser application on the device, which provides a suboptimal user experience.

Ionic & Capacitor (React) - Step 7 - Add logout to your application

Provide a way for your users to log out of your application. Verify that you redirect to Auth0 and then to the address you specified in the returnTo parameter. Check that you are no longer logged in to your application.

-- check that the URL you provided to in the `returnTo` parameter is registered as an allowed callback URL in your Auth0 Dashboard - ::: - :::: +
-## Show the user profile {{{ data-action=code data-code="user-profile.tsx" }}} +
-The Auth0 React SDK retrieves the users profile associated with logged-in users in whatever component you need, such as their name or profile picture, to personalize the user interface. The profile information is available through the `user` property exposed by the `useAuth0()` hook. +

-Initializing the SDK is asynchronous, and you should guard the user profile by checking the `isLoading` and `user` properties. Once `isLoading` is `false` and `user` has a value, the user profile can be used. +## Show the user profile {{{ data-action="code" data-code="user-profile.tsx" }}} -::::checkpoint -:::checkpoint-default -Provide a way for your users to see their user profile details within the app and verify you are able to retrieve and see your profile information on screen once you have logged in. -::: -:::checkpoint-failure -Sorry about that. Here's a couple things to double check: +

The Auth0 React SDK retrieves the user's profile associated with logged-in users in whatever component you need, such as their name or profile picture, to personalize the user interface. The profile information is available through the user property exposed by the useAuth0() hook.

Initializing the SDK is asynchronous, and you should guard the user profile by checking the isLoading and user properties. Once isLoading is false and user has a value, the user profile can be used.

Ionic & Capacitor (React) - Step 8 - Show the user profile

Provide a way for your users to see their user profile details within the app and verify you are able to retrieve and see your profile information on screen once you have logged in.

+ +
+ +

Sorry about that. Here's a couple things to double check:

  • check that you are only reading the user's profile when isLoading is false

  • check that user resolves to an object and is not undefined

-- check that you are only reading the user's profile when `isLoading` is `false` -- check that `user` resolves to an object and is not `undefined` - ::: - :::: +

diff --git a/articles/quickstart/native/ionic-vue/files/LoginButton.md b/articles/quickstart/native/ionic-vue/files/LoginButton.md new file mode 100644 index 0000000000..ceb58fc255 --- /dev/null +++ b/articles/quickstart/native/ionic-vue/files/LoginButton.md @@ -0,0 +1,41 @@ +--- +name: LoginButton.vue +language: javascript +--- + +```javascript + + + + +``` diff --git a/articles/quickstart/native/ionic-vue/files/LogoutButton.md b/articles/quickstart/native/ionic-vue/files/LogoutButton.md new file mode 100644 index 0000000000..6ebfb8809e --- /dev/null +++ b/articles/quickstart/native/ionic-vue/files/LogoutButton.md @@ -0,0 +1,44 @@ +--- +name: LogoutButton.vue +language: javascript +--- + +```javascript + + + +``` diff --git a/articles/quickstart/native/ionic-vue/files/UserProfile.md b/articles/quickstart/native/ionic-vue/files/UserProfile.md new file mode 100644 index 0000000000..0366a88cc4 --- /dev/null +++ b/articles/quickstart/native/ionic-vue/files/UserProfile.md @@ -0,0 +1,35 @@ +--- +name: UserProfile.vue +language: +--- + +``` + + + +``` diff --git a/articles/quickstart/native/ionic-vue/files/app.md b/articles/quickstart/native/ionic-vue/files/app.md index b8129bb76a..1f9bee09d5 100644 --- a/articles/quickstart/native/ionic-vue/files/app.md +++ b/articles/quickstart/native/ionic-vue/files/app.md @@ -1,9 +1,9 @@ --- name: App.vue -language: html +language: javascript --- - -```html + +```javascript