7. Add easy authentication to web app

In this article, add authentication to the React client app, which uses the Static Web app authentication.

Create navigation bar for authentication

Create a navigation component, which provides login and logout functionality.

  1. In VS Code, create a components directory under the React ./app/src directory.

  2. Create a NavBar.tsx file and copy the following code into the file.

    const NavBar = ({user}) => {
    
        const providers = ['twitter', 'github', 'aad'];
        const redirect = `/`;
    
        return (
            <>
            {!user && providers.map((provider) => (
                <span><a key={provider} href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}><h4>{provider}</h4></a> </span>
            ))}
            {user && (
                <div>
                    <p>
                        <span>{user && user?.userDetails} ({user && user?.identityProvider})</span>
                        <span> <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>
                            Logout
                        </a>
                        </span>
                    </p>
                </div>
            )}
            </>
        )
    
    }
    export default NavBar;
    
  3. Create a PublicHome.tsx file and copy the following code into the file:

    import logo from '../logo.svg';
    
    function PublicHome() {
    
        return (
            <div className="App">
                <header className="App-header">
                    <p>
                        Please login with a provider above
                    </p>
                    <img src={logo} className="App-logo" alt="logo" />
    
                </header>
            </div>
        );
    }
    export default PublicHome;
    
  4. Create a PrivateHome.tsx file and copy the following code into the file:

    import React, {useEffect} from 'react';
    import logo from '../logo.svg';
    
    function PrivateHome({user}) {
    
        const [name, setName] = React.useState('');
        const [message, setMessage] = React.useState('');
    
        useEffect(() => {
            if(user && user?.userDetails){
                setName(user?.userDetails);  
            }
          }, [user]);
    
        const getDataFromApi = async (e: any) => {
            e.preventDefault();
            const data = await fetch(`/api/hello?name=${name}`);
            const json = await data.json();
    
            if (json.message) {
                setMessage(json.message);
            }
        };
    
        return (
            <div className="App">
                <header className="App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <p>
                        Static Web App: React App with Azure Function API
                    </p>
                    <form id="form1" className="App-form" onSubmit={e => getDataFromApi(e)}>
                        <div>
                            <input
                                type="text"
                                id="name"
                                className="App-input"
                                placeholder="Name"
                                value={name}
                                onChange={e => setName(e.target.value)} />
                            <button type="submit" className="App-button">Submit</button>
                        </div>
                    </form>
                    <div><h5>Message: {message} </h5></div>
                </header>
            </div>
        );
    }
    export default PrivateHome;
    
  5. Open the ./app/src/App.tsx file and copy the following code into the file:

    import { useState, useEffect } from 'react';
    import './App.css';
    
    // Custom components
    import NavBar from './components/NavBar';
    import PublicHome from './components/PublicHome';
    import PrivateHome from './components/PrivateHome';
    
    function App() {
    
      const [isAuthenticated, userHasAuthenticated] = useState(false);
      const [user, setUser] = useState(null);
    
      useEffect(() => {
        getUserInfo();
      }, []);
    
      async function getUserInfo() {
        try {
    
            const response = await fetch('/.auth/me');
            const payload = await response.json();
            const { clientPrincipal } = payload;
            
            if(clientPrincipal){
              setUser(clientPrincipal);
              userHasAuthenticated(true);
              console.log(`clientPrincipal = ${JSON.stringify(clientPrincipal)}`);
            } 
            
        } catch (error:any) {
            console.error('No profile could be found ' + error?.message?.toString());
        }
    };  
    
      return (
        <div className="App">
          <NavBar user={user}/>
          <main className="column">
            { isAuthenticated ? <PrivateHome user={user}/> : <PublicHome /> }
          </main>
        </div>
      )
    }
    
    export default App;
    

    The highlighted code lines request the current authentication from the /.auth/me route provided by the Static Web Apps environment.

Test the local authentication process provided by SWA CLI

  1. Allow the local app to rebuild and refresh the entire app in the browser, http://localhost:4280.

    Browser screenshot showing the app with authentication provider choices of Twitter, GitHub, and AAD.

  2. Select the GitHub authentication provider.

  3. The local SWA CLI provides an authentication form to use.

    Browser screenshot showing the app with authentication form provided with SWA CLI.

    This form simulates the authentication process for your local development environment. It doesn't call the real authentication providers.

  4. Enter a name and select Login to finish the local authentication process. Control is then returned back to your app and the PrivateHome component is displayed.

    Browser screenshot showing the PrivateHome component because authentication has been provided.

    Both the NavBar and PrivateHome HTML form display the authenticated user name, which is returned from the authentication process.

Commit changes to source control

  1. Check the new app code into your local repo and push to the remote repo:

    git add . && git commit -m "swa authentication" && git push origin main
    
  2. In a web browser, go back to your GitHub repo, and make sure the next build of your Action succeeds with these new changes. The actions URL should look like:

    https://github.com/YOUR-ACCOUNT/staticwebapp-with-api/actions
    
  3. In VS Code, in the Azure explorer, find your static web app, then right-click and select Browse site.

  4. The same React app, as your local version, should appear. The same form functionality as your local version should work, returning a message from the API.

Next steps