<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Auth0 - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Auth0 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 15 May 2026 22:30:39 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/auth0/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build Secure APIs with Flask and Auth0 ]]>
                </title>
                <description>
                    <![CDATA[ APIs are at the heart of modern development. They support all kinds of systems, from mobile, web, and desktop applications, to IoT devices and self-driving cars. They are a bridge between your clients and your application logic and storage.  This cen... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-secure-apis-with-flask-and-auth0/</link>
                <guid isPermaLink="false">66d039d433e91331eb22953b</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Auth0 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authorization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flask Framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Juan Cruz Martinez ]]>
                </dc:creator>
                <pubDate>Wed, 08 Feb 2023 00:32:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>APIs are at the heart of modern development. They support all kinds of systems, from mobile, web, and desktop applications, to IoT devices and self-driving cars. They are a bridge between your clients and your application logic and storage. </p>
<p>This central access point to your application’s data raises the question: how can you provide access to the information to those who need it while denying access to unauthorized requests?</p>
<p>The industry has provided several protocols and best practices for securing APIs. Today we will focus on <a target="_blank" href="https://auth0.com/docs/authorization/protocols/protocol-oauth2?utm_source=freecodecamp?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">OAuth2</a>, one of the most popular options for authorizing clients into our APIs.</p>
<p>But how do we implement OAuth2? There are two ways to go about it:</p>
<ol>
<li>Do it yourself approach</li>
<li>Work with a safe 3rd party like <a target="_blank" href="https://auth0.com/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">Auth0</a></li>
</ol>
<p>In this article, I will walk you through an implementation of OAuth2 for Python and <a target="_blank" href="https://flask.palletsprojects.com/">Flask</a> using Auth0 as our identity provider. But first, we are going to discuss the do-it-yourself approach.</p>
<h2 id="heading-why-not-build-your-own-authentication-and-authorization">Why Not Build Your Own Authentication and Authorization?</h2>
<p>For a few years now, I wanted to give back to the community that helped me so much by teaching me programming and helping me progress in my search for knowledge. I always thought that a great way to contribute was by having my own blog, a thing that I tried more than a few times and failed. </p>
<p>But where did I fail? Instead of focusing on writing, I tried to build my own blog engine because it’s in my nature. It’s what developers do. They love to build.</p>
<p>But why do I mention that here? Because many fall into the same trap when building APIs. Let me explain with an example.</p>
<p>Bob is a great developer, and he has this great idea for a ToDo app that can be the next big thing. Bob is very aware that for a successful implementation, users can only access their own data.</p>
<p>Here is bob’s application timeline:</p>
<ul>
<li>Sprint 0: Research ideas and start prototyping</li>
<li>Sprint 1: Build user table and login screen with API</li>
<li>Sprint 2: Add password reset screens and build all email templates</li>
<li>Sprint 3: Build, create and list ToDos screens</li>
<li>Sprint 4: MVP goes live</li>
<li>User feedback:<ul>
<li>Some users can’t log in due to a bug</li>
<li>Some users feel unsafe without 2-factor authentication</li>
<li>Some users don’t want to get yet another password. They prefer single sign-on with Google or Facebook.</li>
<li>…</li>
</ul>
</li>
</ul>
<p>Let’s talk about what happened. Bob spent the first few sprints not building his app but building the basic blocks, like logging in and out functionality, email notifications, and so on. This valuable time could have been spent differently, but what happens next is more concerning.</p>
<p>Bob’s backlog starts to fill in. Now, he needs to improvise a 2-factor authentication method, add single sign-on, and more non-product-related functions that could potentially delay his product.</p>
<p>And there’s still a big question to be answered: did Bob implement all the security mechanisms correctly? A critical error could expose all the user’s information to outsiders.</p>
<p>What Bob did is what I did with my blog many times. Sometimes, it's helpful to rely on 3rd parties if we want to get things done right.</p>
<p>Today, hackers and attacks have become so sophisticated that security is not a trivial factor anymore. It is a complicated system on its own, and it is often best to leave its implementation to experts – not only so it’s done right, but also so we can focus on what matters: building our applications and APIs.</p>
<h2 id="heading-how-to-set-up-a-free-auth0-identity-management-account">How to Set Up a Free Auth0 Identity Management Account</h2>
<p><a target="_blank" href="https://auth0.com/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">Auth0</a> is a leading authentication and authorization provider, but let’s see how it can help Bob (or you) build a better app:</p>
<ol>
<li>It <a target="_blank" href="https://auth0.com/learn/finn-ai-saves-10-5-ongoing-engineering-time-auth0/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">saves time</a></li>
<li>It’s <a target="_blank" href="https://auth0.com/security?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">secure</a></li>
<li>It has a <a target="_blank" href="https://auth0.com/pricing?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">free plan</a></li>
</ol>
<p>Time to get practical. First, make sure you have an Auth0 account. If not, you can create one <a target="_blank" href="https://auth0.com/signup/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">here for free</a>.</p>
<h3 id="heading-create-a-new-auth0-api">Create a New Auth0 API</h3>
<p>There is still one more thing we have to do before we start coding. Head over to the <a target="_blank" href="https://manage.auth0.com/#/apis?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">APIs</a> section of your Auth0 dashboard and click on the “Create API” button. After that, fill in the form with your details. However, make sure you select <code>RS256</code> as the <code>Signing Algorithm</code>.</p>
<p>Your form should look like the following:</p>
<p><img src="https://lh5.googleusercontent.com/XccGez21ClEDsCECuKwiF_1AF5gj2OXXaJKEXVUOBFmxQ7Ci11a1g1O3cu_io185YbdnSJkAlu3dmP0pt6Ww-N6cPqQLTIeweSi2hNv4ototIkuSZhfiprjqcMrFhcMLaGkKfedkm8D0PR2IcjdLPGUChKS27wsiPMvqCsysQRJyGANVYc5Q5EbFdaFo" alt="Image" width="600" height="400" loading="lazy">
<em>Creating the API – image showing fields to fill out</em></p>
<p>The API details page opens after successfully creating an API. Keep that tab open, as it contains information we need to set up our application. If you close it, don’t worry, you can always access it again.</p>
<h2 id="heading-how-to-bootstrap-our-application">How to Bootstrap our Application</h2>
<p>Because we will focus on the security aspects only, we will take a few shortcuts when building our demo API. However, when developing <a target="_blank" href="https://livecodestream.dev/post/python-flask-api-starter-kit-and-project-layout/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">actual APIs</a>, please follow <a target="_blank" href="https://auth0.com/blog/best-practices-for-flask-api-development/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">best practices for Flask APIs</a>.</p>
<h3 id="heading-install-the-dependencies">Install the dependencies</h3>
<p>First, install the following dependencies for setting up Flask and authenticating users.</p>
<pre><code class="lang-shell">pipenv install flask python-dotenv python-jose flask-cors six
</code></pre>
<h3 id="heading-build-the-endpoints">Build the endpoints</h3>
<p>Our API will be straightforward. It will consist of only three endpoints, all of which, for now, will be publicly accessible. However, we will fix that soon. Here are our endpoints:</p>
<ul>
<li><code>/</code> (public endpoint)</li>
<li><code>/user</code> (requires a logged in user)</li>
<li><code>/admin</code> (only users of admin role)</li>
</ul>
<p>Let’s get to it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask

app = Flask(__name__)

<span class="hljs-meta">@app.route("/")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index_view</span>():</span>
    <span class="hljs-string">"""
    Default endpoint, it is public and can be accessed by anyone
    """</span>
    <span class="hljs-keyword">return</span> jsonify(msg=<span class="hljs-string">"Hello world!"</span>)

<span class="hljs-meta">@app.route("/user")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">user_view</span>():</span>
    <span class="hljs-string">"""
    User endpoint, can only be accessed by an authorized user
    """</span>
    <span class="hljs-keyword">return</span> jsonify(msg=<span class="hljs-string">"Hello user!"</span>)

<span class="hljs-meta">@app.route("/admin")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">admin_view</span>():</span>
    <span class="hljs-string">"""
    Admin endpoint, can only be accessed by an admin
    """</span>
    <span class="hljs-keyword">return</span> jsonify(msg=<span class="hljs-string">"Hello admin!"</span>)
</code></pre>
<p>Very simple right? Let’s run it:</p>
<pre><code class="lang-shell">~ pipenv run flask run
* Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
</code></pre>
<p>And if we access our endpoint:</p>
<pre><code class="lang-shell">~ curl -i http://localhost:5000
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:24:57 GMT

{"msg":"Hello world!"}

~ curl -i http://localhost:5000/user
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 22
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:25:42 GMT

{"msg":"Hello user!"}

~ curl -i http://localhost:5000/admin
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:26:18 GMT

{"msg":"Hello admin!"}
</code></pre>
<h2 id="heading-how-to-secure-the-endpoints">How to Secure the Endpoints</h2>
<p>As we are using OAuth, we will authenticate requests by validating an access token in <a target="_blank" href="https://auth0.com/learn/json-web-tokens/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">JWT format</a>. We'll send it to the API on each request as part of the HTTP headers.</p>
<h3 id="heading-auth0-configuration-variables">Auth0 configuration variables</h3>
<p>As mentioned in the previous section, our API needs to be aware and will require information from our Auth0 dashboard. So head back to your <a target="_blank" href="https://manage.auth0.com/#/apis?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">API details page</a>, and grab two different values.</p>
<p><strong>First, the API Identifier:</strong></p>
<p>This is the value required when the API is created. You can also get it from your API details page:</p>
<p><img src="https://lh5.googleusercontent.com/UffKcasZXNZmXldeB8nhDEjzmOPVao3PR6EUVPbtWzXStuDzcCw2kr5ztEnr0VlWCkBLbhleAM-D11Cv5Cv8fcII8m24D6TfEe4XfxWe8HXN1aNrF-dHeN05zeVeoNfQISWh-VPf0__x8uVfJPL3GGHYIC87utfrr6734Z9Wdk-9eJUApslcdUKOyoSh" alt="Image" width="600" height="400" loading="lazy">
<em>How to find the API identifier on the API details page</em></p>
<p><strong>Next, Auth0 domain:</strong></p>
<p>Unless you're using a custom domain, this value will be <a target="_blank"><code>[TENANT_NAME].auth0.com</code></a>, and you can grab it from the <code>Test</code> tab (make sure not to include <code>https://</code> and the last forward slash <code>/</code>).</p>
<p><img src="https://lh4.googleusercontent.com/cA63NdLr4AWOz2O3jTWBXTTqc7DrGOr1aPOIpNDRYl97-o84I_lX8KtotCm6hRWF06ai0RjiJzgTjS_zRlySKFAB-XO1w737N05i7-bC2-GZioOpcWuS5gaRoEnDL63gXnm5CyP6JOEQusRLQMF1sY_1vjfXtdMVIr5uCW1PMIpokH76lpMq2VFZSIyf" alt="Image" width="600" height="400" loading="lazy">
<em>Getting the Auth0 domain</em></p>
<p>Next, pass those values into variables so they can be used in the validation functions.</p>
<pre><code class="lang-python">AUTH0_DOMAIN = <span class="hljs-string">'YOUR-AUTH0-DOMAIN'</span>
API_IDENTIFIER = <span class="hljs-string">'API-IDENTIFIER'</span>
ALGORITHMS = [<span class="hljs-string">"RS256"</span>]
</code></pre>
<h3 id="heading-error-methods">Error methods</h3>
<p>During this implementation, we will need a way to throw errors when authentication fails. So we will use the following helpers for those needs:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthError</span>(<span class="hljs-params">Exception</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, error, status_code</span>):</span>
        self.error = error
        self.status_code = status_code

<span class="hljs-meta">@app.errorhandler(AuthError)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_auth_error</span>(<span class="hljs-params">ex</span>):</span>
    response = jsonify(ex.error)
    response.status_code = ex.status_code
    <span class="hljs-keyword">return</span> response
</code></pre>
<h3 id="heading-how-to-capture-the-jwt-token">How to capture the JWT token</h3>
<p>The first step to validate a user is to retrieve the JWT token from the HTTP headers. This is very simple, but there are a few things to keep in mind. Here is an example of it:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_token_auth_header</span>():</span>
    <span class="hljs-string">"""
    Obtains the Access Token from the Authorization Header
    """</span>
    auth = request.headers.get(<span class="hljs-string">"Authorization"</span>, <span class="hljs-literal">None</span>)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> auth:
        <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"authorization_header_missing"</span>,
                        <span class="hljs-string">"description"</span>:
                            <span class="hljs-string">"Authorization header is expected"</span>}, <span class="hljs-number">401</span>)

    parts = auth.split()

    <span class="hljs-keyword">if</span> parts[<span class="hljs-number">0</span>].lower() != <span class="hljs-string">"bearer"</span>:
        <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"invalid_header"</span>,
                        <span class="hljs-string">"description"</span>:
                            <span class="hljs-string">"Authorization header must start with"</span>
                            <span class="hljs-string">" Bearer"</span>}, <span class="hljs-number">401</span>)
    <span class="hljs-keyword">elif</span> len(parts) == <span class="hljs-number">1</span>:
        <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"invalid_header"</span>,
                        <span class="hljs-string">"description"</span>: <span class="hljs-string">"Token not found"</span>}, <span class="hljs-number">401</span>)
    <span class="hljs-keyword">elif</span> len(parts) &gt; <span class="hljs-number">2</span>:
        <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"invalid_header"</span>,
                        <span class="hljs-string">"description"</span>:
                            <span class="hljs-string">"Authorization header must be"</span>
                            <span class="hljs-string">" Bearer token"</span>}, <span class="hljs-number">401</span>)

    token = parts[<span class="hljs-number">1</span>]
    <span class="hljs-keyword">return</span> token
</code></pre>
<h3 id="heading-how-to-validate-the-token">How to validate the token</h3>
<p>Having a token passed to our API is a good sign, but it doesn’t mean that it is a valid client. We need to check the token signature.</p>
<p>Since the logic to require authentication can be used for more than one endpoint, it would be important to abstract it and make it easily accessible for developers to implement. The best way to do this is by using <a target="_blank" href="https://book.pythontips.com/en/latest/decorators.html">decorators</a>.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">requires_auth</span>(<span class="hljs-params">f</span>):</span>
    <span class="hljs-string">"""
    Determines if the Access Token is valid
    """</span>
<span class="hljs-meta">    @wraps(f)</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">decorated</span>(<span class="hljs-params">*args, **kwargs</span>):</span>
        token = get_token_auth_header()
        jsonurl = urlopen(<span class="hljs-string">"https://"</span>+AUTH0_DOMAIN+<span class="hljs-string">"/.well-known/jwks.json"</span>)
        jwks = json.loads(jsonurl.read())
        unverified_header = jwt.get_unverified_header(token)
        rsa_key = {}
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> jwks[<span class="hljs-string">"keys"</span>]:
            <span class="hljs-keyword">if</span> key[<span class="hljs-string">"kid"</span>] == unverified_header[<span class="hljs-string">"kid"</span>]:
                rsa_key = {
                    <span class="hljs-string">"kty"</span>: key[<span class="hljs-string">"kty"</span>],
                    <span class="hljs-string">"kid"</span>: key[<span class="hljs-string">"kid"</span>],
                    <span class="hljs-string">"use"</span>: key[<span class="hljs-string">"use"</span>],
                    <span class="hljs-string">"n"</span>: key[<span class="hljs-string">"n"</span>],
                    <span class="hljs-string">"e"</span>: key[<span class="hljs-string">"e"</span>]
                }
        <span class="hljs-keyword">if</span> rsa_key:
            <span class="hljs-keyword">try</span>:
                payload = jwt.decode(
                    token,
                    rsa_key,
                    algorithms=ALGORITHMS,
                    audience=API_IDENTIFIER,
                    issuer=<span class="hljs-string">"https://"</span>+AUTH0_DOMAIN+<span class="hljs-string">"/"</span>
                )
            <span class="hljs-keyword">except</span> jwt.ExpiredSignatureError:
                <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"token_expired"</span>,
                                <span class="hljs-string">"description"</span>: <span class="hljs-string">"token is expired"</span>}, <span class="hljs-number">401</span>)
            <span class="hljs-keyword">except</span> jwt.JWTClaimsError:
                <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"invalid_claims"</span>,
                                <span class="hljs-string">"description"</span>:
                                    <span class="hljs-string">"incorrect claims,"</span>
                                    <span class="hljs-string">"please check the audience and issuer"</span>}, <span class="hljs-number">401</span>)
            <span class="hljs-keyword">except</span> Exception:
                <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"invalid_header"</span>,
                                <span class="hljs-string">"description"</span>:
                                    <span class="hljs-string">"Unable to parse authentication"</span>
                                    <span class="hljs-string">" token."</span>}, <span class="hljs-number">401</span>)

            _request_ctx_stack.top.current_user = payload
            <span class="hljs-keyword">return</span> f(*args, **kwargs)
        <span class="hljs-keyword">raise</span> AuthError({<span class="hljs-string">"code"</span>: <span class="hljs-string">"invalid_header"</span>,
                        <span class="hljs-string">"description"</span>: <span class="hljs-string">"Unable to find appropriate key"</span>}, <span class="hljs-number">401</span>)
    <span class="hljs-keyword">return</span> decorated
</code></pre>
<p>The newly created <code>requires_auth</code> decorator, when applied to an endpoint, will automatically reject the request if no valid user can be authenticated.</p>
<h3 id="heading-how-to-require-an-authenticated-request-for-an-endpoint">How to require an authenticated request for an endpoint</h3>
<p>We are ready to secure our endpoints, let’s update the <code>user</code> and <code>admin</code> endpoints to utilize our decorator.</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route("/user")</span>
<span class="hljs-meta">@requires_auth</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">user_view</span>():</span>
    <span class="hljs-string">"""
    User endpoint, can only be accessed by an authorized user
    """</span>
    <span class="hljs-keyword">return</span> jsonify(msg=<span class="hljs-string">"Hello user!"</span>)

<span class="hljs-meta">@app.route("/admin")</span>
<span class="hljs-meta">@requires_auth</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">admin_view</span>():</span>
    <span class="hljs-string">"""
    Admin endpoint, can only be accessed by an admin
    """</span>
    <span class="hljs-keyword">return</span> jsonify(msg=<span class="hljs-string">"Hello admin!"</span>)
</code></pre>
<p>Our only change was adding <code>@required_auth</code> at the top of the declaration of each endpoint function, and with that we can test once again:</p>
<pre><code class="lang-shell">~ curl -i http://localhost:5000/user
HTTP/1.0 401 UNAUTHORIZED
Content-Type: application/json
Content-Length: 89
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:42:26 GMT

{"code":"authorization_header_missing","description":"Authorization header is expected"}

~ curl -i http://localhost:5000/admin
HTTP/1.0 401 UNAUTHORIZED
Content-Type: application/json
Content-Length: 89
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:42:42 GMT

{"code":"authorization_header_missing","description":"Authorization header is expected"}
</code></pre>
<p>As expected, we can’t access our endpoints as the authorization header is missing. But before we add one, let’s see if our public endpoint still works:</p>
<pre><code class="lang-shell">~ curl -i http://localhost:5000
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:43:55 GMT

{"msg":"Hello world!"}
</code></pre>
<p>Awesome, it works as expected.</p>
<h3 id="heading-how-to-test-it-out">How to test it out</h3>
<p>For testing our newly secured endpoints, we need to get a valid access token that we can pass to the request. We can do that directly on the <code>Test</code> tab on the API details page, and it’s as simple as copying a value from the screen:  </p>
<p><img src="https://lh5.googleusercontent.com/XCAWL5taQUs3_5qcAdukl9FP_aTVLya-jyS_4IivFW6JCAfX5d2hbPPCIV4PB8QgcuceQrzC__YYpWMQB1y8HT9AnKO01XH5rCiofvQJAmiAPnGF42FcJFxaVHTLLQcL9UpzFjYgan0Qasna69DlZ8AIkoATbqAtqtqibWUszhvakHZiytPNduTU7_Hb" alt="Image" width="600" height="400" loading="lazy">
<em>Copying the token for testing</em></p>
<p>Once we have the token we can change our curl request accordingly:</p>
<pre><code class="lang-shell">~ curl -i -H "Authorization: bearer [ACCESS_TOKEN]"  http://localhost:5000/user
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 22
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 22:17:06 GMT

{"msg":"Hello user!"}
</code></pre>
<p>Please remember to replace <code>[ACCESS_TOKEN]</code> with the value you copied from the dashboard.</p>
<p>It works! But we still have some work to do. Even though our <code>/admin</code> endpoint is secured, it can be accessed by any user:</p>
<pre><code class="lang-shell">~ curl -i -H "Authorization: bearer [ACCESS_TOKEN]"  http://localhost:5000/admin
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 22:21:09 GMT

{"msg":"Hello admin!"}
</code></pre>
<h3 id="heading-role-based-access-control">Role-based access control</h3>
<p>For role-based access control there’s a few things we need to do:</p>
<ol>
<li>Create permissions for the API</li>
<li>Enable adding permissions to the JWT for the API</li>
<li>Update the code</li>
<li>Test with users</li>
</ol>
<p>The first 2 points are very well explained in the <a target="_blank" href="https://auth0.com/docs/authorization/rbac/auth-core-features?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">Auth0 docs</a>, so just make sure you add the corresponding permissions on your API. </p>
<p>Next, we need to update the code. We need a function to check if a given permission exists in the access token and return <code>True</code> if it does and <code>False</code> if it does not:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">requires_scope</span>(<span class="hljs-params">required_scope</span>):</span>
    <span class="hljs-string">"""
    Determines if the required scope is present in the Access Token
    Args:
        required_scope (str): The scope required to access the resource
    """</span>
    token = get_token_auth_header()
    unverified_claims = jwt.get_unverified_claims(token)
    <span class="hljs-keyword">if</span> unverified_claims.get(<span class="hljs-string">"scope"</span>):
            token_scopes = unverified_claims[<span class="hljs-string">"scope"</span>].split()
            <span class="hljs-keyword">for</span> token_scope <span class="hljs-keyword">in</span> token_scopes:
                <span class="hljs-keyword">if</span> token_scope == required_scope:
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
</code></pre>
<p>And lastly, it can be used as follows:</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route("/admin")</span>
<span class="hljs-meta">@requires_auth</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">admin_view</span>():</span>
    <span class="hljs-string">"""
    Admin endpoint, can only be accessed by an admin
    """</span>
    <span class="hljs-keyword">if</span> requires_scope(<span class="hljs-string">"read:admin"</span>):
        <span class="hljs-keyword">return</span> jsonify(msg=<span class="hljs-string">"Hello admin!"</span>)

    <span class="hljs-keyword">raise</span> AuthError({
        <span class="hljs-string">"code"</span>: <span class="hljs-string">"Unauthorized"</span>,
        <span class="hljs-string">"description"</span>: <span class="hljs-string">"You don't have access to this resource"</span>
    }, <span class="hljs-number">403</span>)
</code></pre>
<p>Now, only users with the permission <code>read:admin</code> can access our admin endpoint.</p>
<p>In order to test your final implementation, you can follow the steps detailed on <a target="_blank" href="https://auth0.com/docs/quickstart/backend/python/02-using#obtaining-an-access-token?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">obtaining an access token</a> for a given user. </p>
<p>You can also use the Auth0 Dashboard to test permissions, but that is outside the scope of this article. If you would like to learn more about it, read <a target="_blank" href="https://auth0.com/blog/permission-based-security-aspnet-webapi/#Testing-Permissions">here</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Today we learned how to secure a Flask API. We explored the do-it-yourself path, and we built a secure API with three levels of access – public access, private access and privately-scoped access.</p>
<p>There’s so much more that Auth0 can do for your APIs and also for your client applications. Today we just scratched the surface, and it’s up to you and your team when working with real-life scenarios to explore all the potential of their services.</p>
<p>The full code is available on <a target="_blank" href="https://gist.github.com/bajcmartinez/5062aa41ccbe2df1bbf4f1a9b95bd085">GitHub</a>.</p>
<p>Thanks for reading! If you like my teaching style, you can <a target="_blank" href="https://livecodestream.dev/newsletter/">Subscribe to my weekly newsletter</a> for developers and builders and get a weekly email with relevant content<em>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Extend Your Login Flow With Auth0 Actions ]]>
                </title>
                <description>
                    <![CDATA[ I recently attended a training session with the Auth0 Dev Rel team to learn about a cool new feature called Auth0 Actions.  In this article, I am going to explain what Auth0 Actions are, why you'd want to use them, and how to set one up. What are Aut... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/intro-to-auth0-actions/</link>
                <guid isPermaLink="false">66bb4586ce106b2510feda0f</guid>
                
                    <category>
                        <![CDATA[ Auth0 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rohit Jacob Mathew ]]>
                </dc:creator>
                <pubDate>Wed, 22 Dec 2021 15:46:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/rohit-code-idk-2400-x-1260.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I recently attended a training session with the Auth0 Dev Rel team to learn about a cool new feature called Auth0 Actions. </p>
<p>In this article, I am going to explain what Auth0 Actions are, why you'd want to use them, and how to set one up.</p>
<h2 id="heading-what-are-auth0-actions">What are Auth0 Actions?</h2>
<p>Actions are secure, tenant-specific, versioned functions written in Node.js that execute at certain points during the Auth0 runtime. Actions are used to customize and extend Auth0's capabilities with custom logic.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639214635781/VFyOmuqRg.png" alt="&quot;Sample Actions Flow&quot;" width="1600" height="768" loading="lazy"></p>
<p>Above you can see a sample flow. In it, once the user logs into the system, you add a trigger to verify the user's identity using Onfido and then confirm consent using OneTrust before completing the login flow and issuing the token.</p>
<p>In brief, an action is a programmatic way to add custom business logic into your login flow.</p>
<h2 id="heading-why-use-auth0-actions">Why use Auth0 Actions? 🤔</h2>
<p><strong>Extensibility</strong> – they're built to give developers more tooling and a better experience in their login workflows.</p>
<p><strong>Drag N Drop Functionality</strong> – The flow editor lets you visually build custom workflows with drag and drop Action blocks for complete control.</p>
<p><strong>Monaco Code Editor</strong> – Designed with developers in mind, you can easily write JavaScript functions with validation, intelligent code completion, and type definitions with TypeScript support.</p>
<p><strong>Serverless Environment</strong> – Auth0 hosts your custom Action functions and processes them when desired. The functions are stored and run on their infrastructure.</p>
<p><strong>Version Control</strong> – You have the ability to store a history of individual Action changes and the power to revert back to previous versions as needed.</p>
<p><strong>Pre-Production Testing</strong> – Your personal Actions can be drafted, reviewed, and tested before deploying into production</p>
<h2 id="heading-how-to-set-up-auth0-actions">How to Set Up Auth0 Actions</h2>
<p>For the purposes of this demo, we are going to be creating an action to enforce Multi-Factor Authentication (MFA) for a specific role. I will take you through the process of:</p>
<ol>
<li>Creating a role</li>
<li>Adding users</li>
<li>Setting up a demo application</li>
<li>Creating an Action to enforce MFA</li>
<li>Testing the code</li>
</ol>
<p>Let's get started:</p>
<h3 id="heading-1-login-to-your-auth0-account">1) Login to Your Auth0 Account</h3>
<p>The first step to secure your application is to access the Auth0 Dashboard in order to create your Auth0 application. </p>
<p>If you haven’t created an Auth0 account, you can <a target="_blank" href="https://a0.to/signup-for-auth0">sign up for a free one now</a>.</p>
<h3 id="heading-2-create-an-application">2) Create an Application</h3>
<p>Once in the dashboard, move to the Applications tab in the left sidebar.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639214927748/WpImjm7mg.png" alt="Application Page" width="2880" height="1572" loading="lazy"></p>
<p>Click on Create Application.</p>
<p>Provide a friendly name for your application (like Test Actions App) and choose Single Page Web Applications as an application type.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215005392/uhXHjQpPZ.png" alt="Create Application Page" width="2880" height="1572" loading="lazy"></p>
<p>From the quick start tab choose React. Download the sample app. This will have most of the necessary details already in place.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215038833/KmbmIA1nt.png" alt="Quick Start Sample" width="2880" height="1572" loading="lazy"></p>
<p>We also need to set up a few settings for this application. Choose the Settings tab (next to quick start). Add your localhost URL to the following places:</p>
<ol>
<li>Allowed Callback URLs</li>
<li>Allowed Logout URLs</li>
<li>Allowed Web Origins</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215091880/cwD9fJnFd.png" alt="Update Application Settings" width="2880" height="1572" loading="lazy"></p>
<h3 id="heading-3-setup-application">3) Setup Application</h3>
<p>Unzip the code we downloaded in a location of your choice. Then open it in the code editor of your choice.</p>
<p>Cross verify that the details of your application are correctly configured in <code>src/auth_config.json</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screenshot-2021-12-16-at-7.56.39-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We will run this code locally, so install the dependencies and run it in dev mode (so we have hot reload enabled). To do this, <code>npm install &amp; npm run dev</code>.</p>
<p>Once the application starts you should be shown an SPA like below. If you click on Log In it will take you to your login box.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215261508/-E672eefw.png" alt="Sample Application" width="2880" height="1800" loading="lazy"></p>
<h3 id="heading-4-setup-users-and-roles">4) Setup Users and Roles</h3>
<p>Click on the User Management tab in the left sidebar.</p>
<p>Go to the Users tab and click on the Create User button. We need to create 2 users:</p>
<ol>
<li>Admin User</li>
<li>Test User</li>
</ol>
<p>Remember these credentials as these are the test users we will use for this demo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215392817/I51zfr-Ov.png" alt="User Creation" width="2880" height="1572" loading="lazy"></p>
<p>Go to the Roles tab and click on the Create Role button. Call the role <code>Admin</code> and, once it's created, go to the user tab and assign it to your Admin user.</p>
<p>Once this is done go back to your locally running SPA and try logging in with one credential. You should be able to access a user portal like below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215500834/SgGX7vE_5.png" alt="Initial Login" width="2878" height="1796" loading="lazy"></p>
<h3 id="heading-5-setup-actions">5) Setup Actions</h3>
<p>Click on the Actions Tab in the left sidebar. Then go to the Flows category.</p>
<p>Select the Login Flow. This will run the flow of an action once the login process in your login box is complete.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215815525/N-h2y-tlI.png" alt="Login Flow" width="2880" height="1574" loading="lazy"></p>
<p>Click on the <code>+</code> button in Add Action and select Build Custom.</p>
<p>Name it MFA for Role and leave the rest as is.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215793963/Rj2rC2T6f.png" alt="Action Creation Flow" width="2880" height="1570" loading="lazy"></p>
<p>Once created, you'll come to a screen as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215844044/VrPsqFVBz.png" alt="Action Code Editor" width="2870" height="1576" loading="lazy"></p>
<p>Add the below code into the <code>onExecutePostLogin</code> function:</p>
<pre><code>  <span class="hljs-keyword">if</span> (event.authorization != <span class="hljs-literal">undefined</span> &amp;&amp; event.authorization.roles.includes(<span class="hljs-string">"Admin"</span>)) {
      api.multifactor.enable(<span class="hljs-string">"any"</span>);
  };
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215869129/2ELHfGy5s.png" alt="Action Code" width="2880" height="1574" loading="lazy"></p>
<p>On the left side you can see a play button. This is your testing environment inside the actions editor. You will find the <a target="_blank" href="https://auth0.com/docs/actions/triggers/post-login/event-object">event</a> object in which you can test the actions flow by adding <code>Admin</code> to the <code>authorization.roles</code> array.  </p>
<p>When you add the <code>Admin</code> role you should see a response with MFA like below. When it's not present you should get an empty array.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215931493/zai-96biU.png" alt="Action Test Case" width="2880" height="1570" loading="lazy"></p>
<p>Click on save draft and deploy. </p>
<p>Go to the flow now and click on the custom actions tab on the right and you should be able to drag and drop the <code>MFA for Roles</code> action into the flow. Click on Apply so that this new flow will work with your login box.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639215949399/nK49n1ZHZ.png" alt="Action Flow" width="2880" height="1574" loading="lazy"></p>
<p>You will also need to enable MFA on the Auth0 dashboard. </p>
<p>Open the Securities tab and choose multi-factor auth. In the next screen, enable One-time Password. This will let users use an application like Google Authenticator for a one-time password. </p>
<p>There are other factors you can enforce as well, like SMS or Email-based OTP, but for this demo we will be using just the one-time password.  </p>
<p>In the policies section leave everything as is and save your changes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639216209703/f54daE0Jo.png" alt="MFA Screen" width="2880" height="1570" loading="lazy"></p>
<h3 id="heading-6-testing-with-your-application">6) Testing With Your Application</h3>
<p>Now when you go to login on the locally running application, you should be triggered to do a MFA for the admin user. So let's test that.</p>
<p>Click on login and redirect to your login box. If you are logged in already, log out and then do the same.</p>
<p>Enter your admin users credentials:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639216252587/jyNxUdkU9.png" alt="Admin Login" width="2880" height="1800" loading="lazy"></p>
<p>Once the login goes through, you will be prompted to authenticate with your preferred authenticator app. I used google authenticator and entered my OTP.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639216272416/9BGhY_91S.png" alt="Admin MFA" width="2880" height="1796" loading="lazy"></p>
<p>You will then be asked to consent to share your user data with the application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639216291893/v2IITRcrF.png" alt="MFA Consent" width="2880" height="1798" loading="lazy"></p>
<p>Once you accept the above, you should be logged in.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639216404160/YnZZikEzZ.png" alt="Admin Logged In" width="2880" height="1800" loading="lazy"></p>
<p>If you try the same flow with the test user, you will notice that you are directly logged in after the consent page and no MFA request was triggered. </p>
<p>This is because in our actions code, as shown below, you can see that we check to see if the user roles have the Admin role. If so, then we ask Auth0 to trigger am MFA workflow with any of the enabled MFA use cases of the tenant.</p>
<pre><code>  <span class="hljs-keyword">if</span> (event.authorization != <span class="hljs-literal">undefined</span> &amp;&amp; event.authorization.roles.includes(<span class="hljs-string">"Admin"</span>)) {
      api.multifactor.enable(<span class="hljs-string">"any"</span>);
  };
</code></pre><h2 id="heading-conclusion">Conclusion</h2>
<p>Congrats! You have just created a custom Auth0 Actions flow and tested it. This was a simple example to help you understand what Auth0 Actions are, and how they can be built and used in your workflows. </p>
<p>There are many more complex flows you can build, and you can find some examples provided by Auth0 below. Just click on the trigger and you will find specific examples.</p>
<p><a target="_blank" href="https://auth0.com/docs/actions/triggers/">Sample Actions Code</a></p>
<p>Thanks for reading! I really hope that you find this article useful. If so, please share it so others can see it.</p>
<p>Thanks for reading! :)</p>
<p>P.S Do feel free to connect with me on <a target="_blank" href="https://www.linkedin.com/in/rohitjmathew">LinkedIn</a> or <a target="_blank" href="https://twitter.com/iamrohitjmathew">Twitter</a></p>
<h2 id="heading-appendix">Appendix</h2>
<p>The following sources were really helpful in writing this article:</p>
<ul>
<li><a target="_blank" href="https://auth0.com/blog/introducing-auth0-actions/">Introducing Auth0 Actions - Auth0</a></li>
<li><a target="_blank" href="https://auth0.com/docs/actions">Auth0 Actions - Auth0 Docs</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add Authentication to a Vue App Using Auth0 ]]>
                </title>
                <description>
                    <![CDATA[ By Jennifer Bland Auth0 is a flexible, drop-in solution to add authentication and authorization services to your applications. See how easy it is to add to your Vue application so you can register and login users with their email address and a passwo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-authentication-to-a-vue-app-using-auth0/</link>
                <guid isPermaLink="false">66d460cca326133d12440a67</guid>
                
                    <category>
                        <![CDATA[ Auth0 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vue ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 12 Jan 2021 20:11:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/01/0_j4pVnDlQTDvfWtw_-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jennifer Bland</p>
<p>Auth0 is a flexible, drop-in solution to add authentication and authorization services to your applications. See how easy it is to add to your Vue application so you can register and login users with their email address and a password.</p>
<h1 id="heading-what-we-will-be-creating">What we will be creating</h1>
<p>We are going to create a very simple Vue application using the Vue CLI. We will modify the default scaffolded application so that we can use Auth0 to either register a new user or login an existing user. Once a user is logged in then they will have access to view the <strong>About</strong> page.</p>
<p>User’s will be able to register with the application using the email and password authentication system in Auth0.</p>
<h1 id="heading-creating-our-project">Creating our Project</h1>
<p>I will be using the Vue CLI to scaffold out a project for us to start with. To do that you need to have the Vue CLI installed on your system. If you <strong>DO NOT</strong> have it installed, you can install it globally with this command:</p>
<pre><code>npm install -g @vue/cli
</code></pre><p>Now we can use the Vue CLI to create our project. Create a new project using this command:</p>
<pre><code>vue create vue-authentication-auth0
</code></pre><p>You will be asked to pick a preset. Choose “Manually select features” and then select “babel”, “Router” and “Linter / Formatter”.</p>
<p>You will be asked if you want to use history mode for router. Choose “Yes” (should be the default).</p>
<p>You can select any linter you want but for this tutorial I will be selecting “Eslint + Prettier”.</p>
<p>After the Vue CLI is finished, it will give you the commands to change into the new directory that was just created and the command to start the server. Follow those directions. Once the server is started you can open your browser to <code>localhost:8080</code>. You should see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_aULwhJQrKCD0RZ8t.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-how-to-set-up-an-auth0-account">How to Set Up An Auth0 Account</h1>
<p>The first thing you will need to do is to create an account with Auth0 if you don’t already have one. It is free to create an account. You can <a target="_blank" href="https://auth0.com/signup">create your free account here</a>.</p>
<h1 id="heading-how-to-create-our-auth0-application">How to Create our Auth0 Application</h1>
<p>Once you have created your free Auth0 account, login to your account. In the left-hand navigation, click on Applications.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_dx-jheyJRHChE5ET.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From here click on the Create Application button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_zXLpqqkLENVyAASt.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You will be presented with a dialog box for you to provide a name for your application and to specify what type of application you will be creating.</p>
<p>The name of my application is <strong>Vue Authentication Auth0</strong>. You can put whatever you want for the name of your application.</p>
<p>For the application type, select <strong>Single Page Web Application</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_44vQOJ9golspep06.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After your application is created, the Quick Start tab will provide instructions on how to implement Auth0 in your web App using the most popular JavaScript frameworks.</p>
<p>Since we are using Vue.js for our application click on the Vue icon.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_jGC1dkmWfYbUhXQL.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Auth0 provides very detailed instructions on how to implement their Authentication-As-A-Service product. For this tutorial we will implement their instructions in the Vue app that we have already created.</p>
<h1 id="heading-how-to-configure-your-application-settings">How to Configure Your Application Settings</h1>
<p>You can access your settings by clicking on the Settings tab at the top of the page.</p>
<p>You will see your Domain and Client ID under Basic Information. We will come back to this later on because we will need to store these values for our application to work.</p>
<p>Under the Application URIs section we will need to define our <strong>Allowed Callback URLs</strong>, <strong>Allowed Logout URLs</strong>, and <strong>Allowed Web Origins</strong>.</p>
<p>For testing our application locally we will be using the URL of <strong>http://localhost:8080</strong>.</p>
<p><strong>NOTE:</strong> if you decide to host your application somewhere like on Netlify or Heroku then you will need to update all of these settings with the URL of your hosted application.</p>
<p>Set your strong&gt;Allowed Callback URLs, <strong>Allowed Logout URLs</strong>, and <strong>Allowed Web Origins</strong> to be <strong>http://localhost:8080</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_nX1DoL-RXVC1x4oD.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-how-to-install-the-auth0-sdk">How to Install the Auth0 SDK</h1>
<p>Go back to your Vue application and add the Auth0 Client SDK with this command:</p>
<pre><code>npm install @auth0/auth0-spa-js
</code></pre><h1 id="heading-how-to-create-an-authentication-wrapper">How to Create an Authentication Wrapper</h1>
<p>The Auth0 SDK requires that it be initialized before your Vue application has started. </p>
<p>Vue has lifecycle hooks that we could potentially use to initialize the SDK. You might think we could use a <strong>beforeCreate</strong> hook in the <strong>App.vue</strong> file but that won't work. Let me show you why.</p>
<p>Here is an image of the Vue lifecycle hooks.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_NuSvkkm1kohKVJbt.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>beforeCreate</strong> is the very first Vue lifecycle hook to fire. But notice in that image that it fires after the Vue application is created with <strong>new Vue()</strong>.</p>
<p>We need to be able to initialize the Auth0 SDK before the <strong>new Vue()</strong> that creates our Vue application. Vue provides a mechanism to do this with the Vue plugin.</p>
<p>In order to use a plugin you must call it with the <strong>Vue.use()</strong> command. This command must be done before your start your app by calling <strong>new Vue()</strong>.</p>
<p>The Authentication Wrapper we will be creating will actually be a Vue plugin.</p>
<p>In the <strong>src</strong> directory create a new directory called <strong>auth</strong>. Inside that auth directory create a file called <strong>index.js</strong>.</p>
<p>We will copy the code provided from the QuickStart tab and paste it into this file. Here is the code:</p>
<pre><code><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> createAuth0Client <span class="hljs-keyword">from</span> <span class="hljs-string">"@auth0/auth0-spa-js"</span>;
<span class="hljs-comment">/** Define a default action to perform after authentication */</span>
<span class="hljs-keyword">const</span> DEFAULT_REDIRECT_CALLBACK = <span class="hljs-function">() =&gt;</span>
  <span class="hljs-built_in">window</span>.history.replaceState({}, <span class="hljs-built_in">document</span>.title, <span class="hljs-built_in">window</span>.location.pathname);
<span class="hljs-keyword">let</span> instance;
<span class="hljs-comment">/** Returns the current instance of the SDK */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getInstance = <span class="hljs-function">() =&gt;</span> instance;
<span class="hljs-comment">/** Creates an instance of the Auth0 SDK. If one has already been created, it returns that instance */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useAuth0 = <span class="hljs-function">(<span class="hljs-params">{
  onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
  redirectUri = <span class="hljs-built_in">window</span>.location.origin,
  ...options
}</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (instance) <span class="hljs-keyword">return</span> instance;
<span class="hljs-comment">// The 'instance' is simply a Vue object</span>
  instance = <span class="hljs-keyword">new</span> Vue({
    data() {
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">isAuthenticated</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">user</span>: {},
        <span class="hljs-attr">auth0Client</span>: <span class="hljs-literal">null</span>,
        <span class="hljs-attr">popupOpen</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-literal">null</span>
      };
    },
    <span class="hljs-attr">methods</span>: {
      <span class="hljs-comment">/** Authenticates the user using a popup window */</span>
      <span class="hljs-keyword">async</span> loginWithPopup(options, config) {
        <span class="hljs-built_in">this</span>.popupOpen = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.auth0Client.loginWithPopup(options, config);
        } <span class="hljs-keyword">catch</span> (e) {
          <span class="hljs-comment">// eslint-disable-next-line</span>
          <span class="hljs-built_in">console</span>.error(e);
        } <span class="hljs-keyword">finally</span> {
          <span class="hljs-built_in">this</span>.popupOpen = <span class="hljs-literal">false</span>;
        }
<span class="hljs-built_in">this</span>.user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.auth0Client.getUser();
        <span class="hljs-built_in">this</span>.isAuthenticated = <span class="hljs-literal">true</span>;
      },
      <span class="hljs-comment">/** Handles the callback when logging in using a redirect */</span>
      <span class="hljs-keyword">async</span> handleRedirectCallback() {
        <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">true</span>;
        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.auth0Client.handleRedirectCallback();
          <span class="hljs-built_in">this</span>.user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.auth0Client.getUser();
          <span class="hljs-built_in">this</span>.isAuthenticated = <span class="hljs-literal">true</span>;
        } <span class="hljs-keyword">catch</span> (e) {
          <span class="hljs-built_in">this</span>.error = e;
        } <span class="hljs-keyword">finally</span> {
          <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>;
        }
      },
      <span class="hljs-comment">/** Authenticates the user using the redirect method */</span>
      loginWithRedirect(o) {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.auth0Client.loginWithRedirect(o);
      },
      <span class="hljs-comment">/** Returns all the claims present in the ID token */</span>
      getIdTokenClaims(o) {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.auth0Client.getIdTokenClaims(o);
      },
      <span class="hljs-comment">/** Returns the access token. If the token is invalid or missing, a new one is retrieved */</span>
      getTokenSilently(o) {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.auth0Client.getTokenSilently(o);
      },
      <span class="hljs-comment">/** Gets the access token using a popup window */</span>
getTokenWithPopup(o) {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.auth0Client.getTokenWithPopup(o);
      },
      <span class="hljs-comment">/** Logs the user out and removes their session on the authorization server */</span>
      logout(o) {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.auth0Client.logout(o);
      }
    },
    <span class="hljs-comment">/** Use this lifecycle method to instantiate the SDK client */</span>
    <span class="hljs-keyword">async</span> created() {
      <span class="hljs-comment">// Create a new instance of the SDK client using members of the given options object</span>
      <span class="hljs-built_in">this</span>.auth0Client = <span class="hljs-keyword">await</span> createAuth0Client({
        ...options,
        <span class="hljs-attr">client_id</span>: options.clientId,
        <span class="hljs-attr">redirect_uri</span>: redirectUri
      });
<span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// If the user is returning to the app after authentication..</span>
        <span class="hljs-keyword">if</span> (
          <span class="hljs-built_in">window</span>.location.search.includes(<span class="hljs-string">"code="</span>) &amp;&amp;
          <span class="hljs-built_in">window</span>.location.search.includes(<span class="hljs-string">"state="</span>)
        ) {
          <span class="hljs-comment">// handle the redirect and retrieve tokens</span>
          <span class="hljs-keyword">const</span> { appState } = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.auth0Client.handleRedirectCallback();
<span class="hljs-comment">// Notify subscribers that the redirect callback has happened, passing the appState</span>
          <span class="hljs-comment">// (useful for retrieving any pre-authentication state)</span>
          onRedirectCallback(appState);
        }
      } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">this</span>.error = e;
      } <span class="hljs-keyword">finally</span> {
        <span class="hljs-comment">// Initialize our internal authentication state</span>
        <span class="hljs-built_in">this</span>.isAuthenticated = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.auth0Client.isAuthenticated();
        <span class="hljs-built_in">this</span>.user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.auth0Client.getUser();
        <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>;
      }
    }
  });
<span class="hljs-keyword">return</span> instance;
};
<span class="hljs-comment">// Create a simple Vue plugin to expose the wrapper object throughout the application</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Auth0Plugin = {
  install(Vue, options) {
    Vue.prototype.$auth = useAuth0(options);
  }
};
</code></pre><h1 id="heading-how-to-create-a-config-file">How to Create a Config File</h1>
<p>The options object passed to the plugin is used to provide the values for <strong>clientId</strong> and <strong>domain</strong> which I mentioned earlier and said we would get to later.</p>
<p>In the root directory of your application create a new file called <strong>auth_config.json</strong>. We will populate the values from your application for <strong>domain</strong> and <strong>clientId</strong>. Put this code into auth_config.json file and be sure to update it with the values for your application.</p>
<pre><code>{   
  <span class="hljs-string">"domain"</span>: <span class="hljs-string">"yourAppValuesHere"</span>,   
  <span class="hljs-string">"clientId"</span>: <span class="hljs-string">"yourAppValuesHere"</span>
}
</code></pre><p>This configuration file contains non-sensitive values relating to your Auth0 application. This file should not be committed into source control. We can do that by adding the filename to the <strong>.gitignore</strong> file.</p>
<p>Open the <strong>.gitignore</strong> file and add <code>auth_config.json</code> in the file.</p>
<h1 id="heading-how-to-add-the-plugin-to-our-vue-application">How to Add the Plugin to Our Vue Application</h1>
<p>Now that we have created our plugin we need to tell Vue to use it. Open up the <strong>main.js</strong> file. Add these two import statements which import our plugin as well as our domain and clientId from the <strong>auth_config.json</strong> file.</p>
<pre><code><span class="hljs-comment">// Import the Auth0 configuration</span>
<span class="hljs-keyword">import</span> { domain, clientId } <span class="hljs-keyword">from</span> <span class="hljs-string">"../auth_config.json"</span>;
<span class="hljs-comment">// Import the plugin here</span>
<span class="hljs-keyword">import</span> { Auth0Plugin } <span class="hljs-keyword">from</span> <span class="hljs-string">"./auth"</span>;
</code></pre><p>Next we need to tell Vue to use our plugin. After the import statements add this code:</p>
<pre><code><span class="hljs-comment">// Install the authentication plugin here</span>
Vue.use(Auth0Plugin, {
  domain,
  clientId,
  <span class="hljs-attr">onRedirectCallback</span>: <span class="hljs-function"><span class="hljs-params">appState</span> =&gt;</span> {
    router.push(
      appState &amp;&amp; appState.targetUrl
        ? appState.targetUrl
        : <span class="hljs-built_in">window</span>.location.pathname
    );
  }
});
</code></pre><h1 id="heading-how-to-login-to-the-app">How to Login to the App</h1>
<p>If you look at the plugin code in the <strong>auth/index.js</strong> file you will notice that there are two different login methods provided: <strong>loginWithPopup</strong> and <strong>loginWithRedirect</strong>.</p>
<p>Auth0 provides a hosted login page that any application can use to login or register users for their application. </p>
<p>The <strong>loginWithRedirect</strong> method will access the hosted login page. That means that when users click the login button the URL will change to point to the Auth0 website where the user will enter their login details. After they have successfully authenticated they will be redirected back to our application.</p>
<p>If we don’t want to do this redirect, Auth0 provides the option to login or register users via a popup that shows on our website.</p>
<p>I will show you how to use both of these login methods.</p>
<p>Open up the <strong>App.vue file</strong>. The nav currently has two entries for the Home and About pages. We need to add two buttons to Login. Add this code in the nav which should look like this:</p>
<pre><code>&lt;div id=<span class="hljs-string">"nav"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home <span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span></span>|
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span></span> |
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!$auth.loading"</span>&gt;</span>
    |
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"login"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!$auth.isAuthenticated"</span>&gt;</span>
      Login
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    |
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"loginPopup"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!$auth.isAuthenticated"</span>&gt;</span>
      Login Popup
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    |<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/div&gt;
</code></pre><p>Notice that the buttons are wrapped in a directive that makes sure <strong>$auth.loading</strong> is false. If you review the code for our plugin there is a data section with a value of <strong>isAuthenticated</strong>. This value is set if a user successfully authenticates with Auth0. If the user is authenticated then we do not want to show the two login buttons.</p>
<p>When we add the div then the buttons appear on the row below the links for the Home and About button. I want them to all be on the same line so I update the CSS styles to be this:</p>
<pre><code>#nav { 
  <span class="hljs-attr">display</span>: flex; 
  justify-content: center; 
  padding: <span class="hljs-number">30</span>px; 
} 
#nav a { 
  font-weight: bold; 
  color: #<span class="hljs-number">2</span>c3e50; 
  padding: <span class="hljs-number">0</span> <span class="hljs-number">5</span>px; 
}
</code></pre><p>Now when you view the application you will see the two buttons.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_LyHE1bybFhyso-Db.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The two buttons are calling methods <strong>login</strong> and <strong>loginPopup</strong>. Let’s implement them now.</p>
<p>Add a methods object with two methods. Here is the code:</p>
<pre><code>methods: { 
  login() { 
    <span class="hljs-built_in">this</span>.$auth.loginWithRedirect(); 
  }, 
  loginPopup() { 
    <span class="hljs-built_in">this</span>.$auth.loginWithPopup(); 
  }, 
}
</code></pre><p>The <strong>this.$auth</strong> is a handle for our plugin. We are then calling the methods available in our plugin.</p>
<p>Now go back to your application. If you click the login button you should be taken to Auth0’s hosted login page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_BopphiRqeQ3ReXlI.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you click on the Login Popup button you will see a login modal in your application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_e40CPdd19xjobDw-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Regardless of which one you choose, you will see that you have the option to either log in or sign up. Go ahead and create an account. When you return to the application you will see that both the login buttons are hidden. They are hidden because the <strong>isAuthenticated</strong> value in the plugin is now true.</p>
<h1 id="heading-how-to-implement-logout">How to Implement Logout</h1>
<p>The next step is to implement a Logout. Open up the <strong>App.vue</strong> file. Add a button for logout like this:</p>
<pre><code>&lt;button @click=<span class="hljs-string">"logout"</span> v-<span class="hljs-keyword">if</span>=<span class="hljs-string">"$auth.isAuthenticated"</span>&gt;
  Logout
&lt;/button&gt;
</code></pre><p>Here we have a directive to only show this button if the user is currently authenticated. Go back to your application and you should now see the Logout button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/0_qp0QOUNUP-wz3iUn--1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Add this method to implement the logout functionality:</p>
<pre><code>logout() { 
  <span class="hljs-built_in">this</span>.$auth.logout(); 
  <span class="hljs-built_in">this</span>.$router.push({ <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span> }); 
}
</code></pre><p>In this method we call the logout function in our plugin. In case the user was on a page that is only visible to users that are authenticated, we redirect the user to the home page.</p>
<h1 id="heading-how-to-only-show-pages-to-authenticated-users">How to Only Show Pages to Authenticated Users</h1>
<p>Currently our application on has a Home page and an About page. Instead of creating a new page, let’s set the About page to be only visible if a user is logged in.</p>
<p>We only want to show the About page in the nav if the user is logged in. We will take the same directive we use for displaying the Logout button and put it on the About page in the nav. Update the nav to be this:</p>
<pre><code>&lt;router-link v-<span class="hljs-keyword">if</span>=<span class="hljs-string">"$auth.isAuthenticated"</span> to=<span class="hljs-string">"/about"</span>&gt;About&lt;/router-link&gt;
</code></pre><h1 id="heading-how-to-add-a-route-guard">How to Add a Route Guard</h1>
<p>We have hidden the link to the About page in the nav if a user is not currently authenticated. But a user can type in the url <strong>/about</strong> to go directly to the page. This shows that an unauthenticated user can access that page. You can avoid this by using a route guard.</p>
<p>In the auth directory create a new file called <strong>authGuard.js</strong>.</p>
<p>Add this code to the file:</p>
<pre><code><span class="hljs-keyword">import</span> { getInstance } <span class="hljs-keyword">from</span> <span class="hljs-string">"./index"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authGuard = <span class="hljs-function">(<span class="hljs-params">to, <span class="hljs-keyword">from</span>, next</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> authService = getInstance();
<span class="hljs-keyword">const</span> fn = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// If the user is authenticated, continue with the route</span>
    <span class="hljs-keyword">if</span> (authService.isAuthenticated) {
      <span class="hljs-keyword">return</span> next();
    }
<span class="hljs-comment">// Otherwise, log in</span>
    authService.loginWithRedirect({ <span class="hljs-attr">appState</span>: { <span class="hljs-attr">targetUrl</span>: to.fullPath } });
  };
<span class="hljs-comment">// If loading has already finished, check our auth state using `fn()`</span>
  <span class="hljs-keyword">if</span> (!authService.loading) {
    <span class="hljs-keyword">return</span> fn();
  }
<span class="hljs-comment">// Watch for the loading property to change before we check isAuthenticated</span>
  authService.$watch(<span class="hljs-string">"loading"</span>, <span class="hljs-function"><span class="hljs-params">loading</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (loading === <span class="hljs-literal">false</span>) {
      <span class="hljs-keyword">return</span> fn();
    }
  });
};
</code></pre><p>This code checks to see if the user is currently authenticated. If they are not it brings up the Auth0 hosted login page for the user to login. If the user fails to login or is not able to successfully login, then it redirects the user away from the page they were trying to access that has the route guard.</p>
<p>Now let’s implement this route guard in our Vue router. Open up the <strong>index.js</strong> file in the router directory.</p>
<p>At the top of the file add an import for the authGuard file we just created:</p>
<pre><code><span class="hljs-keyword">import</span> { authGuard } <span class="hljs-keyword">from</span> <span class="hljs-string">"../auth/authGuard"</span>;
</code></pre><p>Next we need to add the route guard to the /about route. Update the /about route to be this:</p>
<pre><code>{ 
  <span class="hljs-attr">path</span>: <span class="hljs-string">'/about'</span>, 
  <span class="hljs-attr">name</span>: <span class="hljs-string">'About'</span>, 
  <span class="hljs-attr">component</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-comment">/* webpackChunkName: "about" */</span> <span class="hljs-string">'../views/About.vue'</span>), 
  <span class="hljs-attr">beforeEnter</span>: authGuard 
}
</code></pre><p>Go back to your application. If you are not currently authenticated then login to your application. You should see the About entry in the Nav. Now logout of the application. Manually try to go the url <strong>/about</strong>. You should be redirected to the Auth0 hosted login page.</p>
<p>Congratulations! You have successfully added Auth0 authentication to your Vue application.</p>
<h1 id="heading-get-the-code">Get The Code</h1>
<p>I have the <a target="_blank" href="https://github.com/ratracegrad/Vue-Auth0-Authentication-Tutorial">complete code in my GitHub account here</a>. If you get the code please do me a favor and star my repo. Thank you!</p>
<h1 id="heading-using-other-authentication-methods">Using Other Authentication Methods</h1>
<p>I have written several follow up articles on adding Authentication to your Vue application using other authentication methods.</p>
<p>Want to use Firebase for authentication, <a target="_blank" href="https://www.freecodecamp.org/news/how-to-add-authentication-to-a-vue-app-using-firebase/">read this article</a>.</p>
<p>Want to use AWS Amplify for authentication, <a target="_blank" href="https://www.freecodecamp.org/news/how-to-add-authentication-to-a-vue-app-using-aws-amplify/">read this article</a>.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Auth0 is an Authentication-As-A-Service product that you can add to your application. It provides very easy to use Authentication.</p>
<p>Hope you enjoyed this article. If you like it please share it. Thanks for reading. And you can read more of my tutorials on <a target="_blank" href="https://www.jenniferbland.com/">my personal website</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to set up GatsbyJS authentication with Auth0 ]]>
                </title>
                <description>
                    <![CDATA[ By Michael Ozoemena TL;DR GatsbyJS is a framework that uses GraphQL and ReactJS to enable you to create feature-rich, super fast and dynamic web apps. It gives you the ability to consume data from virtually anywhere and use them in your app. In this ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-gatsbyjs-authentication-with-auth0-d07abdd5a4f4/</link>
                <guid isPermaLink="false">66c35481c7095d76345eaf5c</guid>
                
                    <category>
                        <![CDATA[ Auth0 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 11 Mar 2019 21:40:07 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*vaizCYVmspYXc7W-Yavprw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Michael Ozoemena</p>
<h4 id="heading-tldr">TL;DR</h4>
<p>GatsbyJS is a framework that uses GraphQL and ReactJS to enable you to create feature-rich, super fast and dynamic web apps. It gives you the ability to consume data from virtually anywhere and use them in your app. In this tutorial, I’ll be showing you how to use Auth0 which is an authentication and authorization platform to add authentication to your GatsbyJS application and to your serverless function on Netlify.</p>
<p>I’ll assume you have at least a basic understanding of React, Node, and GraphQL.</p>
<p>Here’s <a target="_blank" href="https://github.com/THEozmic/micro-blog">the Github Repository</a> if you want to take a look at the source code.</p>
<h4 id="heading-enter-gatsbyjs">Enter GatsbyJS.</h4>
<p>Created in 2015, <a target="_blank" href="https://www.gatsbyjs.com">Gatsby</a> is a simple way to build a website with React. Today, Gatsby is known to be used to build websites like blogs, portfolio pages, and even e-commerce applications. Gatsby sites are known for being blazingly fast, and that is because when you build a website using Gatsby, it comes with tons of performance optimizations out-the-box, unlike some other frontend frameworks that leave you to figure out how to make your website more performant. Gatsby’s secret to being fast is in the fact that it follows the PRPL architectural pattern, which stands for:</p>
<ul>
<li><strong>Push</strong> critical resources for the initial URL route using <code>&lt;link prelo</code>ad&gt;and http/2.</li>
<li><strong>Render</strong> initial route.</li>
<li><strong>Pre-cache</strong> remaining routes.</li>
<li><strong>Lazy-load</strong> and create remaining routes on demand.</li>
</ul>
<p>It is a pattern developed by Google for structuring and serving Progressive Web Apps (PWAs), with an emphasis on the performance of app delivery and launch. You can <a target="_blank" href="https://developers.google.com/web/fundamentals/performance/prpl-pattern/">read more about this pattern here</a>.</p>
<h4 id="heading-what-is-auth0">What is Auth0?</h4>
<p><a target="_blank" href="https://auth0.com">Auth0</a>, pronounced as “Auth Zero” is a robust authentication and authorization platform. It makes it super easy to do add things like user registration, password retrieval, sign in, social login, multi-factor authentication, enterprise login, single sign-on, and more, into your production application.</p>
<p>Auth0 pays close attention to the developer’s experience with their excellent blogs posts and robust and easy to understand documentation. With Auth0 you can make use of various identity standards:</p>
<ul>
<li><strong>OAuth 1</strong></li>
<li><strong>OAuth 2</strong></li>
<li><strong>Open ID Connect</strong></li>
<li><strong>JSON Web Tokens (JWT)</strong></li>
<li><strong>Security Assertion Markup Language (SAML)</strong></li>
<li><strong>WS-Federation</strong></li>
</ul>
<p>In this tutorial, we’ll focus on using a combination of <strong>JSON Web Tokens</strong> and Social logins with <strong>OAuth 2</strong>.</p>
<h4 id="heading-serverless-functions-and-netlify">Serverless Functions and Netlify.</h4>
<p>Netlify is a platform that lets you deploy your project without worrying about certain overheads like Continuous Deployment, HTTP Certs, and <a target="_blank" href="https://www.netlify.com/features/">more</a>, created as a way to deploy and manage static websites that don’t have a backend.</p>
<p>Now, because not everyone wants to deploy a static website and there’s the need for support for a backend, Netlify added a feature called “Serverless functions” which are backends where you don’t have to worry about the server infrastructure.</p>
<p>Behind the scenes, Netlify functions stand between you and something called Amazon Web Services (AWS) Lambda which is where the real “serverless” happens, and it lives on Amazon’s AWS cloud platform. Netlify functions help simplify things for you, so you don’t have to deal directly with AWS and also be able to keep using all of Netlify’s other cool features like Continuous Deployment.</p>
<p>The word “serverless” doesn’t imply without a server; it means that you as a developer need not worry about the server infrastructure (physical and otherwise).</p>
<p>You can read more about <a target="_blank" href="https://www.netlify.com/">Netlify</a> and their <a target="_blank" href="https://www.netlify.com/features/functions/">Serverless Functions</a>.</p>
<h4 id="heading-our-app-micro-blog">Our App: Micro Blog.</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*RgCKVGa1FIaFvPnUI-9CCA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Our app is called “Micro Blog”. It’s a platform that allows users to create short, frequent posts. Each post contains some text content, username and profile image of the person making the post.</p>
<p>Anyone can open the web app and view posts of every other person, but to make a post themselves, they need to log in. The app supports social logins and an email login.</p>
<p>If you are already familiar with most of this stuff and want to see the code, you can head over to <a target="_blank" href="https://github.com/THEozmic/micro-blog">the source code on Github</a>.</p>
<h4 id="heading-building-the-front-end">Building the Front-End.</h4>
<p>Our front-end is a GatsbyJS app, and that means the first thing we need to do is install the Gatsby CLI node package from npm.</p>
<p><strong>Note:</strong> Use node version “&gt;= 6.9.0 &lt;7.0.0 || &gt;= 8.9.0” else you get an error when you try to create a new Gatsby site, this is because of the dependency “css-loader@1.0.1”.</p>
<pre><code># install the Gatsby CLI globallynpm install -g gatsby-cli
</code></pre><pre><code># create a <span class="hljs-keyword">new</span> Gatsby site using the <span class="hljs-keyword">default</span> startergatsby <span class="hljs-keyword">new</span> micro-blog
</code></pre><p>After the commands are done running, you should be able to enter into the directory called “micro-blog”, relative to where you executed the commands above.</p>
<pre><code>cd micro-blog
</code></pre><p>When you take a look at the contents of this directory, you’ll find a ton of generated content. At this point, you can fire up your Gatsby site and see it working. To do that, in your terminal run this:</p>
<pre><code>gatsby develop
</code></pre><p>This will start up your Gatsby site on <code>[http://localhost:8000/](http://localhost:8000/)</code>.</p>
<p>Next step is to add and modify content specific to our application.</p>
<p>We’ll start with <code>gatsby-config.js</code>. Replace the contents of the file with:</p>
<p>You might want to update the “” author placeholder value.</p>
<p>This file contains your Gatsby app settings, stuff like your site metadata and plugins. It’s a pretty important file Gatsby looks for when you start up your app. Within the app, we can use GraphQL to query the contents of this file.</p>
<p>Up next, <code>src/components/header.js</code>:</p>
<p>This file is our shared header component. Now, a few things are going on here:</p>
<ul>
<li>We are importing some things from <code>gatsby</code> library: <code>Link</code> and <code>navigate</code>. <code>Link</code> is a React component used to link to other pages that are within your app like “/app/home”, while <code>navigate</code> is a function that accepts a URL and programmatically navigates the user to the specified URL.</li>
<li><code>isLoggedIn</code>, <code>logout</code>, and <code>getUserNickname</code> are methods that check if the user is logged in, log out the user, and get a logged in user’s nickname for display purposes, respectively.</li>
<li><code>Button</code> is a component that displays a button element for the user. It accepts several props that help us easily give the button varying looks.</li>
</ul>
<p>Here’s what <code>Button</code> looks like:</p>
<p>As you’ll see, we are going to be making use of <a target="_blank" href="https://emotion.sh/">Styled Components</a> a lot and specifically <code>emotion</code>, which is one of the many supported CSS-in-JS packages for GatsbyJS.</p>
<p>Later, we will take a look at <code>src/services/auth.js</code>.</p>
<p>Next important file to look at is <code>/src/components/layout.js</code>:</p>
<p>This file is the wrapper file for our application. It includes the header, a footer, and renders children passed to it. We also see the imported <code>graphql</code> package from <code>gatsby</code> alongside <code>StaticQuery</code> component. <code>StaticQuery</code> accepts a <code>query</code> prop which is a GraphQL query. Whatever value resolved from the <code>query</code> is made available in the <code>StaticQuery</code> component’s render prop.</p>
<p>Taking a closer look at the query, we can see that it’s getting data from the <code>gatsby-config.js</code> file.</p>
<p>Our accompanying CSS <code>/src/components/layouts.css</code> stays almost the same with the generated one with the only difference being from line 8:</p>
<pre><code>body {
</code></pre><pre><code>   margin: <span class="hljs-number">0</span>;
</code></pre><pre><code>   background-color: #f2f9ff;
</code></pre><pre><code>}
</code></pre><p>Let’s leave the <code>/src/components</code> directory for now and take a look at <code>/src/pages/index.js</code> :</p>
<p>All files in <code>/src/pages/</code> become pages in your Gatsby App. For example, <code>index.js</code> becomes the homepage and <code>/src/pages/app/home.js</code> becomes <code>[http://yourdomain.com/app/home](http://yourdomain.com/app/home)</code>.</p>
<p>On our homepage, we want our users to see the recent posts and ask them to log in or sign up if they want to create a post.</p>
<p>To get our recent posts, we need <code>axios</code>, which is a promise-based library for making network requests in JavaScript. Install <code>axios</code> by running this in your terminal:</p>
<pre><code>npm install axios
</code></pre><p>When our component mounts, we check if the user is logged in and we redirect them to <code>/app/home</code> because we don’t want them being on this page if they are logged in. Admittedly, this is a pretty naïve approach, and we could make use of “Protected Routes” instead. Using “Protected Routes” means that this component will not even get the chance to be mounted at all. Due to the small size of this project, I’ve decided not to make use of Protected Routes.</p>
<p>In case you want to implement Protected Routes in your Gatsby App, please refer to <a target="_blank" href="https://www.gatsbyjs.org/docs/authentication-tutorial/#creating-client-only-routes">this guide</a> on the official Gatsby website.</p>
<p>We create a request to get the posts when our component mounts and then update the state with the returned data. Updating the state causes our component re-render the child<code>RecentPosts</code> component since it makes use of the said state.</p>
<p>Notice that the URI in the network request to fetch the posts data is an environment variable <code>process.env.API_URI</code>. These environment variables aren’t the typical environment variables you find in a Node app. To create these environment variables, you need two files in your Gatsby application root directory: <code>env.production</code> and <code>env.development</code>. These files will be automatically loaded by Gatsby in the appropriate environment when you start up your app.</p>
<p>As I mentioned earlier, these environment variables aren’t the same with your Node environment variables and what makes them different is that they aren’t private files that you typically exclude in your <code>.gitignore</code> file. You have to push these files when you want to deploy your app because GatsbyJS will need to read these files on startup.</p>
<p>Mine looks something like:</p>
<pre><code>AUTH0_DOMAIN=micro-blog.auth0.com
</code></pre><pre><code>AUTH0_CLIENTID=cIovhIQvYOr6fk3yhDtKjB5EiIvLevxf
</code></pre><pre><code>REDIRECT_URI=<span class="hljs-string">'http://localhost:8000/callback'</span>
</code></pre><pre><code>API_URI=<span class="hljs-string">'http://localhost:9000/.netlify/functions/'</span>
</code></pre><p>In production, it’s a bit different:</p>
<pre><code>AUTH0_DOMAIN=micro-blog.auth0.com
</code></pre><pre><code>AUTH0_CLIENTID=cIovhIQvYOr6fk3yhDtKjB5EiIvLevxf
</code></pre><pre><code>REDIRECT_URI=<span class="hljs-string">'https://angry-shaw-7a81ce.netlify.com/callback'</span>
</code></pre><pre><code>API_URI=<span class="hljs-string">'https://angry-shaw-7a81ce.netlify.com/.netlify/functions/'</span>
</code></pre><p>In order to get these values for your own app, you need to <a target="_blank" href="https://auth0.com/signup">create an Auth0 account</a> if you don’t already have one.</p>
<p>Note that you can use Auth0 for free with limited features.</p>
<p>After you’ve created an account, log in to your Auth0 <a target="_blank" href="https://manage.auth0.com/">management dashboard</a> and create a new Auth0 Application. You can do that by clicking on the Applications menu item and then the <strong>Create Application</strong> button. You can update the application name from “My App” to whatever else you want to use. You can change this later if you wish. In my case, I use “Micro Blog”.</p>
<p>Next, you select “Single Page Web App” and click on <strong>Create</strong>. This will immediately create your application.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*i_bqW8WOT-LaCOYYUNMoFA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once you are done with creating your application, you should navigate to “Settings”, there you will find your <code>**AUTH0_CLIENTID**</code> and <code>**AUTH0_DOMAIN**</code> values.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*-l_4flPcJdcwMW9Boj2jiA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>Note:</strong> For your <code>.env.production</code>, you don’t have the <code>**REDIRECT_URI**</code> and <code>**API_URI**</code> at this point. Later on, after we create our Netlify app, we will get the application URL which we can then put in there appropriately.</p>
<p>Now, let’s take a look at <code>src/components/recentPosts.js</code>:</p>
<p>Again, here we make use of Styled Components. We also make use of a React lifecycle method <code>shouldComponentUpdate</code> to prevent unnecessary re-renders when the <code>RecentPosts</code> component is used in another component.</p>
<p>When a user clicks on the login button, we navigate the user to an Auth0 login page. After they have been authenticated, we redirect the user to a URL in our app called <code>/callback</code> where we check that the user has been logged in properly and then save their details in <code>localStorage</code>. Here’s what the <code>/callback</code> page looks like:</p>
<p>We call the <code>handleAuthentication</code> method which will get data from the URL, parse it, save the extracted data to <code>localStorage</code> and then call the <code>() =&gt; naviage('/app/hom</code>e') method to redirect the user to the main app.</p>
<p>Now, we take a look at the <code>/pages/app/home.js</code> page, where only logged in users can access:</p>
<p>There isn’t much that is new here. The only things I’d mention are:</p>
<ul>
<li>We create new posts in the <code>handlePostSubmit</code> method and in there, we make a regular <code>axios</code> call but with a <code>headers</code> option containing the JWT token “id_token”. We do that because, in our Serverless Function, we will be needing that value in the headers to authenticate a request, making sure that only a logged in user can create a new post and that the token saved on the client side is actually valid and not tampered with. This greatly improves the security and reliability of our app.</li>
<li>We redirect the user to <code>/</code> when they aren’t logged in properly and they manage to land on this page. We do that in the <code>componentDidMount</code> lifecycle method. Again, <a target="_blank" href="https://www.gatsbyjs.org/docs/authentication-tutorial/#creating-client-only-routes">Protected Routes</a> are a better option in your production apps.</li>
</ul>
<p>Finally, we get to <code>/src/services/auth.js</code>. We have been using functions from this file throughout the app, it’s time to take a look at it:</p>
<p>In this file we use <code>auth0-js</code> a JavaScript library by Auth0 to handle the authentication parts of our app. Add it to your app by running this in your terminal:</p>
<pre><code>npm install auth0-js
</code></pre><p>Next thing you see in this file is the creation of <code>isBrowser</code> which states if our file is currently being executed within the context of a browser. This is important because during the build process you might run into errors trying to call things like <code>window.localStorage</code>.</p>
<p>Let’s take a look at some of the methods in this file:</p>
<p>The <code>getUser</code> method gets the user details from the access token previously stored in the <code>localStorage</code>, after our user has been logged in. It uses the <code>getAccessToken</code> method to fetch the access token stored.</p>
<p>The <code>handleLogin</code> method is called when the user tries to log in. It redirects them to the Auth0 login page which in turn redirects the user to <code>/callback</code> once they’ve been logged in.</p>
<p>The <code>isLoggedIn</code> method checks that the JWT token “id_token” expiration date saved in <code>localStorage</code> as <code>expiresAt</code> hasn’t been exceeded, thereby invalidating the user’s session.</p>
<p>The <code>handleAuthentication</code> method is what you see being used in the <code>/callback</code> page. This method parses the URL hash and gets important values which we later save in <code>localStorage</code> in the <code>setSession</code> method.</p>
<p>Finally, the <code>logout</code> method logs the user out by deleting saved credentials. This works well but you could take it a step further by <a target="_blank" href="https://auth0.com/docs/api/authentication#logout">calling an endpoint on Auth0</a> which will invalidate the session completely. I stopped here for the sake of this tutorial.</p>
<p>Finally, we update line 6 on <code>/src/components/seo.js</code>:</p>
<pre><code><span class="hljs-keyword">const</span> SEO = ({ description = <span class="hljs-literal">null</span>, lang = <span class="hljs-string">"eng"</span>, meta = [], keywords = [], title }) =&amp;gt; {
</code></pre><p>Making it use an ES6 arrow function and default values.</p>
<h4 id="heading-building-the-back-end">Building the Back-End.</h4>
<p>Next, we are going to build an API to serve a list of posts and to collect new posts. They are serverless functions hosted on Netlify. Our API needs to do a few things:</p>
<ul>
<li>Have an endpoint to serve the list of posts: <code>/.netlify/functions/postsRead</code>.</li>
<li>Have an endpoint to collect new posts: <code>/.netlify/functions/postsCreate</code>.</li>
<li>Authenticate requests to create new posts using Auth0.</li>
</ul>
<p>To get started, we need to install a few npm packages:</p>
<pre><code>npm install netlify-lambda mongoose jwks-rsa jsonwebtoken dotenv
</code></pre><p>Next step is to create a directory called <code>utils</code> in the root directory of our Gatsby App. Inside that directory is where our files that aren’t quite the API will live. One of such files is our <code>/utils/db.js</code> file:</p>
<p>In this file, we establish a connection to our MongoDB database.</p>
<p>Something missing here is our <code>.env</code> file (yes, a third one!). Mine looks something like this:</p>
<pre><code>DATABASE_PROD=<span class="hljs-string">'mongodb://&lt;username&gt;:&lt;password&gt;@&lt;db_url&gt;'</span>DATABASE_DEV=<span class="hljs-string">'mongodb://localhost:27017/micro-blog'</span>
</code></pre><p>I use <a target="_blank" href="https://mlab.com">mLab</a> to host my database online and I have <a target="_blank" href="https://www.mongodb.com/">MongoDB</a> installed on my development machine. You can follow this <a target="_blank" href="https://docs.mongodb.com/v3.2/administration/install-community/">guide to install MongoDB</a> on your development machine too.</p>
<p>The next file to focus on is <code>/utils/index.js</code>, this file contains some other methods we will make use of in our Netlify functions.</p>
<p>The first method <code>respondWith</code> is abstracting the logic of responding to requests that get to our Netlify functions. And the second method <code>verifyToken</code> is verifying that the tokens sent in the headers of requests are valid.</p>
<p>Finally, into the Netlify functions. Create a new directory in your application root called <code>functions</code> (or anything else you find appealing) and in that directory, create three files:</p>
<ul>
<li><code>postsCreate.js</code></li>
<li><code>postsRead.js</code></li>
<li><code>postsModel.js</code></li>
</ul>
<p>The first two files will hold our implementation for creating and reading posts while the last file will describe our Posts Database Schema.</p>
<p>Here’s what the <code>postsModel.js</code> looks like:</p>
<p>And <code>postsCreate.js</code>:</p>
<p>Lastly, <code>postsRead.js</code>:</p>
<p>Now, to run our functions locally, we first create a new script in our <code>package.json</code> file:</p>
<pre><code><span class="hljs-string">"scripts"</span>: {<span class="hljs-comment">// other scripts</span>
</code></pre><pre><code><span class="hljs-string">"start:lambda"</span>: <span class="hljs-string">"NODE_ENV=development netlify-lambda serve functions"</span>
</code></pre><pre><code>}
</code></pre><p>I use “serve functions” because the “functions” directory is where I put my Netlify functions, yours could be different.</p>
<p>After creating that script, we run it in our terminal:</p>
<pre><code>npm run start:lambda
</code></pre><h4 id="heading-deploying-the-app"><strong>Deploying the App.</strong></h4>
<p>The last thing we will do is to deploy our app to Netlify and to do that we need to first create a file on our application root called <code>netlify.toml</code>. This file is a configuration file which Netlify will read while trying to build and deploy the app. Here’s what that file looks like:</p>
<pre><code>[build]  functions = <span class="hljs-string">"lambda"</span>  Command = <span class="hljs-string">"npm run prod"</span>
</code></pre><p>The <code>functions = lambda</code> instructs Netlify put the built functions in a folder called “lambda”. And the <code>Command = "npm run prod"</code> specifies a script to run in order to build the entire app. This is pretty important because we need to build both our Gatsby App and our Netlify functions. Here’s what that script looks like in our <code>package.json</code>:</p>
<pre><code><span class="hljs-string">"scripts"</span>: {
</code></pre><pre><code><span class="hljs-comment">// previous scripts</span>
</code></pre><pre><code><span class="hljs-string">"build:lambda"</span>: <span class="hljs-string">"netlify-lambda build functions"</span>,
</code></pre><pre><code><span class="hljs-string">"prod"</span>: <span class="hljs-string">"NODE_ENV=production npm run build; npm run build:lambda"</span>
</code></pre><p>Here, we first run <code>npm run build</code> which builds our Gatsby App and then run <code>npm run build:lambda</code> which builds our Netlify functions. Again, here I use “functions” because that is the name of the folder where I put my Netlify functions.</p>
<p>After doing all that, we create a new Github Repository and push our code there. <a target="_blank" href="https://app.netlify.com/signup">Create a new Netlify account</a> if you don’t already have one. I prefer using the Github signup option in this case. When you are logged in, you click on the <strong>New site from Git</strong> button which will then take you through the process of creating a new Netlify app.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*2qwvW3hrPYdewA7WPRPedg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If during the process of creating a new Netlify app, you don’t find your repository in the list shown, be sure that you have given Netlify access to all your repositories or at least that repository in particular.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*JUqE4yBnsBu-I5gOH4yNVA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Before you deploy, click on the <strong>Show Advanced</strong> button and create a new variable called <code>DATABASE_PROD</code>, setting the value to what’s in your <code>.env</code> file. Remember that this file is excluded from your app in your <code>.gitignore</code> so there’s no way for your app to read this value unless you do this.</p>
<p>Also, add <code>public/</code> as the Publish directory since that is the directory where Gatsby builds and dumps the files.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*n0H9KJFp5bQoIZoaX8VqEg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Netlify will automatically handle deploying the Functions. After the app has been deployed, you should see the URL of your app on your dashboard.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*eivTZZNT6DcTMvAg8JczoA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And now that you have the app URL, you can update your <code>.env.production</code> file accordingly.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
