Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .auth0/lab/guides/implement-web-sign-in.tour
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
{
"file": "src/spa-app/app.js",
"description": "## Implement Web Sign-In <app.js>\nThis **_app.js_** file contains the code that starts the SPA in the browser. In it you will see an import statement followed by an [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) (Immediately Invoked Function Expression) containing the logic to handle window lifecycle events. \n\nIt registers two event listeners to handle the hash change and load events passing them both to a router function. \n\nFinally, the application uses session storage to detect when the user clicks the refresh button in their browser. \n\nThis code is executed once when the page is loaded. We will create the Auth0 client here and make it available to the rest of the application by attaching it to the window instance. \n\nInsert the following code:\n\n``` jsx\nconst domain = window.env.AUTH0_DOMAIN;\nconst clientId = window.env.CLIENT_ID;\nconst redirect_uri = window.env.APP_URL;\n\nwindow.auth0Client = await auth0.createAuth0Client({\n domain,\n clientId,\n authorizationParams: {\n redirect_uri,\n },\n});\n```\n\n\nThis code pulls the Auth0 domain, client id, and the application URL from environment variables. The Application URL is constructed in the services/environment.js file. \n\nThe redirect\\_uri property defines the URL that Auth0 must call after the authentication phase is concluded. This is the same URL listed in the **Allowed Callback URLs** field in the Auth0 Dashboard. If you use another URL without whitelisting it first, Auth0 will show an error page when you attempt to login. ",
"line": 4,
"line": 14,
"selection": {
"start": {
"line": 4,
Expand All @@ -35,7 +35,7 @@
{
"file": "src/spa-app/router.js",
"description": "## Implement Web Sign-In <router.js> (1/2)\nThis file contains the code that is responsible for navigating between the various views of the SPA application. At the top of it, you will see import statements for three views Home, Expenses, and Error404. The import for the Navbar component is special in that it is shared among the three views. \n\nNext is a simple map between client-side routes and the views they correspond to. Followed by two constants that reference the DOM elements that the navbar and content should be rendered. \n\nFinally, a router function is defined to render the views based on the hash value of the browser window. \n\nNext, we'll implement the authentication callback as the first step of the router logic. \n\nClick Insert Code to add the callback handler below:\n\n``` jsx\nif (new URLSearchParams(window.location.search).has(\"code\")) {\n await window.auth0Client.handleRedirectCallback();\n window.history.replaceState({}, document.title, \"/\");\n}\n```\n\n\nThis logic checks the querystring of the current URL for an authorization code. This code can be used to identify if the request in question refers to a user coming back from the authentication process ( i.e. if this is a user being redirected back to the application by Auth0 after authenticating). If present, it calls the handleRedirectCallback function of the Auth0 client. Upon success, the URL is updated to remove the authorization code and the function continues processing. \n\nIn the event that the user is authenticated, we want to get the user’s profile so that it can be rendered instead of the default anonymous profile. \n\nProceed when you're ready! ",
"line": 15,
"line": 18,
"selection": {
"start": {
"line": 15,
Expand All @@ -50,7 +50,7 @@
{
"file": "src/spa-app/router.js",
"description": "## Implement Web Sign-In <router.js> (2/2)\nNext, we'll add code that uses the Auth0 client to check the authentication status of the current session, fetch the user's profile, and attach it to the window instances.\n\n``` jsx\nif (await window.auth0Client.isAuthenticated()) {\n window.user = await window.auth0Client.getUser();\n}\n```\n\nProceed when you're ready!",
"line": 20,
"line": 22,
"selection": {
"start": {
"line": 20,
Expand Down Expand Up @@ -95,7 +95,7 @@
{
"file": "src/spa-app/views/components/Navbar.js",
"description": "## Adjust the 'postRender' logout call\nTo define what happens when users click the logout button, we'll insert the following code here: \n\n``` jsx\n window.auth0Client.logout({\n logoutParams: {\n returnTo: window.env.APP_URL,\n },\n });\n```\n\nIn this case, we are making the Logout invoke the `logout()` function provided by the SPA SDK to end the user's Auth0 session. The returnTo property passed to this method works similar to the redirect\\_uri. This logout return URL was allow-listed in your Auth0 Application using the **Allowed Logout URLs** field. ",
"line": 47,
"line": 51,
"selection": {
"start": {
"line": 47,
Expand Down Expand Up @@ -129,4 +129,4 @@
"description": "## Log in to the SPA and view Expenses!\nNice work!\n\nLet's do a little housekeeping, then re-deploy everything to take a look.\n\n1. #### Cleanup and re-deploy:\n 1. #### Click [here](command:workbench.action.files.saveAll) to save all of the progress you've made.\n 1. #### Click [here](command:workbench.action.debug.stop) to stop the previous version of the SPA, and [here](command:workbench.action.debug.stop) to stop the previous version of the API.\n 1. #### Now, click [here](command:workbench.action.debug.start) to re-deploy the new versions.\n 1. #### Click [here](command:auth0.lab.openEndpointByName?[\"Lab: SPA, Lab: API\"]) to launch the SPA and API in your browser.\n\n1. #### Explore the SPA App.\n You will notice that the App now allows you to see the home page as an anonymous user. \n\n ![](https://cdn.auth0.com/website/training/example/IDFUN-M04-L01-Ex01/img-1.png) \n\n1. #### Click the **_Expenses_** link. \n Notice that the page appears to refresh, but you have been redirected back to the homepage. \n\n1. #### Click the **_Login_** link to start the authentication process. \n\n1. #### Log in with any end-user account that you created for these labs. \n\n After successful authentication, Auth0 will redirect you back to your app. The homepage now addresses you by name and your profile image will be shown near the upper-right corner: \n\n ![](https://cdn.auth0.com/website/training/example/IDFUN-M04-L01-Ex01/img-2.png) \n\nCongrats! You have successfully authenticated to the single page application! There are some caveats you need to be aware of at this point.\n\n## **_Some caveats_**:\n\n1. #### If you log in using a social identity provider (Google, Facebook, etc.), you will experience odd behavior like broken images when you refresh the SPA. This happens because you are using Auth0’s test development keys for the identity provider. \n\n To prevent this from happening, you would need to register your application with the relevant Identity Provider and replace the test development keys on the Auth0 dashboard with your own. \n\n However, for the purposes of this lab, you should log in with a username and password to avoid the aforementioned behavior. \n\n For more information, see [Test Social Connections with Auth0 Developer Keys](https://auth0.com/docs/connections/social/devkeys). \n\n1. #### By default, the Auth0 SPA SDK holds authentication information in memory. Refreshing the browser causes the browser to start a new session clearing all the information stored in memory. \n\n The Auth0 SPA SDK will attempt to [silently authorize](https://auth0.com/docs/authorization/configure-silent-authentication#initiate-silent-authentication-requests) the user when the client is created. This functionality relies on the use of cookies created by the authorization server to determine if the user is currently authenticated. \n In the context of the SPA application, these cookies are considered third-party to the host. Some browsers like Safari and Brave have strict rules concerning the use of third-party cookies. \n\n The net result is that when you refresh the browser depending on which browser you are using that silent authentication may fail and the user will appear to no longer be authenticated and will have to authenticate again. \n \n As an example, here is what the silent authentication call looks like in Chrome Developer Tools. Notice the cookies named **_did_** and **_Auth0_** included in the request to the authorize endpoint. \n\n ![](https://cdn.auth0.com/website/training/example/IDFUN-M04-L01-Ex01/img-27.png) \n\n Here is the same request using Safari. The cookies are not sent! \n\n ![](https://cdn.auth0.com/website/training/example/IDFUN-M04-L01-Ex01/img-21.png) \n\n If you need to support users using restrictive browsers, Auth0 recommends using [Refresh Token Rotation](https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation), which provides a secure method for using refresh tokens in SPAs while providing end-users with seamless access to resources without the disruption in UX caused by browser privacy technology like ITP."
}
]
}
}
14 changes: 12 additions & 2 deletions src/spa-app/app.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import router from "./router";

(async function () {
// 👉 Replace this with client creation 👈

const domain = window.env.AUTH0_DOMAIN;
const clientId = window.env.CLIENT_ID;
const redirect_uri = window.env.APP_URL;

window.auth0Client = await auth0.createAuth0Client({
domain,
clientId,
authorizationParams: {
redirect_uri,
},
});

// handle user navigation
window.addEventListener("hashchange", () => {
router();
Expand Down
4 changes: 2 additions & 2 deletions src/spa-app/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
Expand All @@ -23,7 +23,7 @@
<div id="content" class="content">
<h1>Loading...</h1>
</div>
<!-- 👉 Replace this with auth0-spa-sdk reference 👈 -->
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.0/auth0-spa-js.production.js"></script>
<script src="app.js"></script>
</body>
</html>
11 changes: 8 additions & 3 deletions src/spa-app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ const navbar = document.getElementById("navbar");
const content = document.getElementById("content");

const router = async () => {
// 👉 Replace this with callback handler 👈

// 👉 Replace this with user profile handler 👈
if (new URLSearchParams(window.location.search).has("code")) {
await window.auth0Client.handleRedirectCallback();
window.history.replaceState({}, document.title, "/");
}

if (await window.auth0Client.isAuthenticated()) {
window.user = await window.auth0Client.getUser();
}

const request = location.hash.slice(1).toLowerCase() || "/";
const page = routes[request] || Error404;
Expand Down
10 changes: 7 additions & 3 deletions src/spa-app/views/components/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ window.user = {

const Navbar = {
render: async () => {
const isAuthenticated = false; // 👈 Replace this with isAuthenticated check
const isAuthenticated = await window.auth0Client.isAuthenticated();
const view = /*html*/ `
<li class="logo">
<a href="#">
Expand Down Expand Up @@ -39,12 +39,16 @@ const Navbar = {
postRender: async () => {
document.getElementById("log-in").addEventListener("click", async (e) => {
e.preventDefault();
// 👉 Replace this with login call 👈
await window.auth0Client.loginWithRedirect();
});

document.getElementById("log-out").addEventListener("click", (e) => {
e.preventDefault();
// 👉 Replace this with logout call 👈
window.auth0Client.logout({
logoutParams: {
returnTo: window.env.APP_URL,
},
});
});
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/spa-app/views/pages/Expenses.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import expensesApi from "../../services/expensesApi.js";
import Table from "../components/Table.js";

const Expenses = {
allowAccess: async () => false, // 👈 Replace this with isAuthenticated check
allowAccess: async () => window.auth0Client.isAuthenticated(),
render: async () => {
const expenses = await expensesApi.getReports();
const view = /*html*/ `
Expand Down