Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Microsoft Azure profile imageAaron Powell
Aaron Powell forMicrosoft Azure

Posted on • Originally published ataaron-powell.com on

     

Adding User Profiles to Static Web Apps

With Azure Static Web Apps we get auser profile as part of the security platform, but that profile is pretty limited, we get an ID for the user and something contextual from the authentication provider, like an email address or a username. This means that if we want to create a more enriched user profile, we need to do it ourselves.

So, let’s take a look at how we can do that. For this demo, I’m going to use theReact SWA template, the npm package@aaronpowell/react-static-web-apps-auth and@aaronpowell/static-web-apps-api-auth. We’re also only going to use GitHub as the authentication provider, but the pattern displayed here is applicable to any authentication provider (you’d just need to figure out the appropriate APIs).

Authenticating a user

First we're going to need some way to log the user in, or at least, checking that they are logged in, so we'll wrap the whole application in theClientPrincipalContextProvider component:

// updated index.jsxReactDOM.render(<React.StrictMode><ClientPrincipalContextProvider><App/></ClientPrincipalContextProvider></React.StrictMode>,document.getElementById("root"));
Enter fullscreen modeExit fullscreen mode

Having thisContextProvider means that we'll be able to use theuseClientPrincipal React Hook (which the package ships with) to check if the user is logged in or not within our application, and that'll be critical to make the right decisions throughout the app.

Let's rewrite theApp component to use theuseClientPrincipal hook:

functionApp(){constdetails=useClientPrincipal();if(!details.loaded){return(<section><h1>Loading...</h1></section>);}// todoreturnnull;}
Enter fullscreen modeExit fullscreen mode

Theloaded property of the Hook state is indicating whether or not we're received a response from the/.auth/me endpoint, which is what we use to determine if someone is authenticated to our app, if they're authenticated, we'll get the standard profile back, if not, we'll get a null profile. Once this has completed we can check for aclientPrincipal:

functionApp(){constdetails=useClientPrincipal();if(!details.loaded){return(<section><h1>Loading...</h1></section>);}if(!details.clientPrincipal){return<Login/>;}// todoreturnnull;}
Enter fullscreen modeExit fullscreen mode

We'll create a basicLogin component that:

functionLogin(){return(<section><h1>Login</h1><StaticWebAuthLoginsazureAD={false}twitter={false}/></section>);}
Enter fullscreen modeExit fullscreen mode

This uses the component from@aaronpowell/react-static-web-apps-auth and disabled Azure AD and Twitter, which are part of thepre-configured providers.

Getting the GitHub user info

Before we can finish off the UI component, we need some way in which we can get the user’s information from GitHub. Let’s do that by adding a new API to our SWA:

import{AzureFunction,Context,HttpRequest}from"@azure/functions";importfetch,{Headers}from"node-fetch";import{getUserInfo,isAuthenticated}from"@aaronpowell/static-web-apps-api-auth";consthttpTrigger:AzureFunction=asyncfunction(context:Context,req:HttpRequest):Promise<void>{if(!isAuthenticated(req)){context.res={status:401};return;}constuserInfo=getUserInfo(req);constheaders=newHeaders();headers.append("accept","application/json");headers.append("user-agent","azure-functions");headers.append("authorization",`Basic${Buffer.from(`${process.env.GitHubUsername}:${process.env.GitHubToken}`).toString("base64")}`);constres=awaitfetch(`https://api.github.com/users/${userInfo.userDetails}`,{headers});if(!res.ok){constbody=awaitres.text();context.res={status:res.status,body};return;}const{login,avatar_url,html_url,name,company,blog,location,bio,twitter_username}=awaitres.json();context.res={body:{login,avatar_url,html_url,name,company,blog,location,bio,twitter_username}};};exportdefaulthttpTrigger;
Enter fullscreen modeExit fullscreen mode

The first thing this function is going to do is check that there is a logged in user, using theisAuthenticated function from the@aaronpowell/static-web-apps-api-auth package (you don’t need to do this if you configure SWA to require the call to be authenticated, but I tend to do it out of habit anyway).

Assuming they are logged in, we’ll make a call to the GitHub API to get the user’s details. It’d be a good idea to provide an authentication token to do this, so you don’t get rate limited.Aside: I’m usingBuffer.from("...").toString("base64") notbtoa to do the encoding, as at the time of writing the API that SWA deploys runs Node.js ~12, andbtoa was added to Node.js in ~14.

How do we know the user to access? TheclientPrincipal that we get back has theuserDetails field set to the GitHub username, so we can use that in the API call.

And then assuming that’s successful, we’ll return the fields that are we care about back to the client.

<GitHubIdentityContextProvider>

We're going to build a new React Context (+ Provider) so that we can finish off ourApp like so:

functionApp(){constdetails=useClientPrincipal();if(!details.loaded){return(<section><h1>Loading...</h1></section>);}if(!details.clientPrincipal){return<Login/>;}return(<GitHubIdentityContextProvider><User/></GitHubIdentityContextProvider>);}
Enter fullscreen modeExit fullscreen mode

We'll create a new file calledGitHubIdentityContextProvider.tsx and start creating our context provider:

import{useClientPrincipal}from"@aaronpowell/react-static-web-apps-auth";importReact,{createContext,useContext}from"react";typeGitHubUser={login:string;avatar_url:string;html_url:string;name:string;company:string;blog:string;location:string;bio:string;twitter_username:string;};constGitHubIdentityContext=createContext<GitHubUser|null>(null);
Enter fullscreen modeExit fullscreen mode

First thing, let's create a TypeScript type for the user, obviously skip this if you're not using TypeScript.

We'll then create our React Context usingcreateContext and call itGitHubIdentityContext. We're not going to export this from the module, as we don't want people creating their own providers using it, we want to do that for them, so we can control how it populates the profile data.

Now for the Context Provider:

constGitHubIdentityContextProvider=({children}:any)=>{constswaUser=useClientPrincipal();const[githubUser,setGitHubUser]=React.useState<GitHubUser|null>(null);React.useEffect(()=>{if(swaUser.loaded&&swaUser.clientPrincipal){fetch("/api/user-details").then(res=>res.json()).then(setGitHubUser);}},[swaUser]);return(<GitHubIdentityContext.Providervalue={githubUser}>{children}</GitHubIdentityContext.Provider>);};
Enter fullscreen modeExit fullscreen mode

TheGitHubIdentityContextProvider is a React Component, which uses theuseClientPrincipal Hook and tracks the GitHub user details as local state. We'll use an effect Hook to wait for the profile to be loaded, and if it has been, call the new API that we created earlier in this post (I called mineuser-details). Unpack the response as JSON and push it into state, now we have the GitHub user info available to our client.

Lastly, we'll create a custom Context Hook to expose this and export them from our module.

constuseGitHubUser=()=>useContext(GitHubIdentityContext);export{GitHubIdentityContextProvider,useGitHubUser};
Enter fullscreen modeExit fullscreen mode

The<User /> component

With the GitHub profile ready, we can create a<User /> component to render the information out:

functionUser(){constgithubUser=useGitHubUser();if(!githubUser){returnnull;}return(<div><h1>{githubUser.name}</h1><h2>                Works at{githubUser.company} in{githubUser.location}</h2><p>{githubUser.bio}</p><ul><li><ahref={githubUser.html_url}>Profile</a></li><li><ahref={`https://twitter.com/${githubUser.twitter_username}`}>                        Twitter</a></li><li><Logout/></li></ul></div>);}
Enter fullscreen modeExit fullscreen mode

With anull check to ensure it isn't used in the wrong place (and to satisfy the TypeScript compiler that we aren't using anull object 😜) we can dump out the profile in whatever format we want.

And there we have it, an Azure Static Web App with authentication provided by GitHub, along with a rich user profile.

You can check out the full sampleon my GitHub, along with adeployed version of the sample.

Static Web Apps GitHub Identity Sample

This repository contains a sample application showing how you can create your own user profile using the GitHub API from within Static Web Apps.

Learn moreon my blog andcheck out the deployed app.

Azure Static Website React Template

This repository contains a template for creating anAzure Static Web App projects using React + TypeScript.

In the template there isCreate React App site using TypeScript and anapi folder with an emptyAzure Functions, also using TypeScript.

To get started, click theUse this template button to create a repository from this template, and check out theGitHub docs on using templates.

Running The Application

From a terminal runnpm start from both the repository root andapi folder to start the two servers, the web application will be onhttp://localhost:3000 and the API onhttp://localhost:7071. Alternatively…




Conclusion

Static Web Apps does a good job of giving us the building blocks for creating an authenticated experience. In this post we've looked at how we can take those building blocks and create a rich user profile, provided by the underlying GitHub API.

Although this sample is GitHub centric, there's no reason you can't apply the pattern against any other authentication provider, including custom ones. You could even make an API that looks at theidentityProvider property of theclientPrincipal and call Azure AD, Twitter, or any other provider in use.

I'd also suggest that you explore how you can effectively cache this data locally, either in a user store in Azure, or in the browser usinglocalStorage orsessionStorage, but there are privacy considerations and data purging to think of, which is beyond the scope of what I wanted to cover in this post.

Hopefully this helps you create apps with richer user profiles.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Invent with purpose

Any language. Any platform.

More fromMicrosoft Azure

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp