적용 대상: 워크포스 테넌트 흰색 확인 표시가 있는 녹색 원 외부 테넌트(Green circle with a white check mark symbol.자세히 알아보기)
이 자습서에서는 인증을 위해 JavaScript SPA(단일 페이지 애플리케이션)를 구성합니다. 이 시리즈 1부에서는 JavaScript SPA를 만들고 인증을 위해 준비했습니다. 이 자습서에서는 앱에 msAL(Microsoft 인증 라이브러리) 구성 요소를 추가하고 앱에 대한 반응형 UI(사용자 인터페이스)를 빌드하여 인증 흐름을 추가하는 방법을 알아봅니다.
이 자습서에서는 다음을 수행합니다.
- auth.js 인증 흐름을 처리하는 코드 추가
- 애플리케이션에 대한 사용자 인터페이스 빌드
필수 구성 요소
리디렉션 파일에 코드 추가
인증 흐름은 애플리케이션이 사용자를 인증하는 데 걸리는 일련의 단계입니다. Auth.js 리디렉션 또는 팝업 방법을 사용하여 로그인 및 로그아웃을 포함하여 인증 흐름을 처리하는 데 사용되는 함수를 포함합니다.
public/auth.js 열고 다음 코드를 추가합니다.
// Browser check variables: If you support IE, our recommendation is to sign-in using Redirect APIs. If you are testing using Edge InPrivate mode, please add "isEdge" to the if check. const ua = window.navigator.userAgent; const msie = ua.indexOf("MSIE "); const msie11 = ua.indexOf("Trident/"); const msedge = ua.indexOf("Edge/"); const isIE = msie > 0 || msie11 > 0; const isEdge = msedge > 0; let signInType; let accountId = ""; // myMSALObj instance - configuration parameters are located at authConfig.js const myMSALObj = new msal.PublicClientApplication(msalConfig); myMSALObj.initialize().then(() => { // Redirect: once login is successful and redirects with tokens, call Graph API myMSALObj.handleRedirectPromise().then(handleResponse).catch(err => { console.error(err); }); }) function selectAccount() { /** * See here for more info on account retrieval: * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md */ const currentAccounts = myMSALObj.getAllAccounts(); if (!currentAccounts) { return; } else if (currentAccounts.length > 1) { // Add your account choosing logic here console.warn("Multiple accounts detected."); } else if (currentAccounts.length === 1) { username = currentAccounts[0].username showWelcomeMessage(currentAccounts[0].username); updateTable(currentAccounts[0]); } } function handleResponse(resp) { if (resp !== null) { accountId = resp.account.homeAccountId; myMSALObj.setActiveAccount(resp.account); showWelcomeMessage(resp.account); } else { selectAccount(); } } async function signIn(method) { signInType = isIE ? "redirect" : method; if (signInType === "popup") { return myMSALObj.loginPopup({ ...loginRequest, redirectUri: "/redirect" }).then(handleResponse).catch(function (error) { console.log(error); }); } else if (signInType === "redirect") { return myMSALObj.loginRedirect(loginRequest) } } function signOut(interactionType) { const logoutRequest = { account: myMSALObj.getAccountByHomeId(accountId) }; if (interactionType === "popup") { myMSALObj.logoutPopup(logoutRequest).then(() => { window.location.reload(); }); } else { myMSALObj.logoutRedirect(logoutRequest); } } // This function can be removed if you do not need to support IE async function getTokenRedirect(request, account) { return await myMSALObj.acquireTokenSilent(request).catch(async (error) => { console.log("silent token acquisition fails."); if (error instanceof msal.InteractionRequiredAuthError) { // fallback to interaction when silent call fails console.log("acquiring token using redirect"); myMSALObj.acquireTokenRedirect(request); } else { console.error(error); } }); }
파일을 저장합니다.
애플리케이션에 대한 사용자 인터페이스 빌드
권한 부여가 구성되면 프로젝트를 실행할 때 애플리케이션과 상호 작용하도록 UI를 만들 수 있습니다. 부트스트랩로그인 및 로그아웃 단추가 포함된 반응형 UI를 만드는 데 사용됩니다. 또한 UI에는 토큰의 클레임을 표시하는 테이블이 포함되어 있으며, 이 테이블은 자습서의 뒷부분에서 추가됩니다.
index.html 파일에 코드 추가
SPA의 기본 페이지인 index.html애플리케이션이 시작될 때 로드되는 첫 번째 페이지입니다. 또한 사용자가 로그아웃 단추를 선택할 때 로드되는 페이지이기도 합니다. 페이지에는 탐색 모음, 사용자 전자 메일이 포함된 환영 메시지 및 토큰의 클레임을 표시하는 테이블이 포함되어 있습니다.
public/index.html 열고 다음 코드 조각을 추가합니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <title>Microsoft identity platform</title> <link rel="SHORTCUT ICON" href="./favicon.svg" type="image/x-icon"> <link rel="stylesheet" href="./styles.css"> <!-- adding Bootstrap 5 for UI components --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous"> <!-- msal.min.js can be used in the place of msal-browser.js --> <script src="/msal-browser.min.js"></script> </head> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bootstrap 5 Navbar</title> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Rl3l94faQlfdS3b8SpxyDkgOn+Y5Qu3og6JpNZnN9LfX9k8wAI5" crossorigin="anonymous"> </head> <body> <!-- Navbar --> <nav class="navbar navbar-expand-sm navbar-dark bg-primary navbarStyle"> <a class="navbar-brand" href="/">Microsoft identity platform</a> <div class="ms-auto d-flex align-items-center"> <!-- Dropdown group (Bootstrap 5 uses dropstart instead of dropleft) --> <div class="btn-group dropstart"> <!-- Toggle button for dropdown --> <button id="signIn" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" > Sign In </button> <!-- Dropdown menu --> <div class="dropdown-menu"> <button class="dropdown-item" id="popup" onclick="signIn(this.id)"> Sign in using Popup </button> <button class="dropdown-item" id="redirect" onclick="signIn(this.id)"> Sign in using Redirect </button> </div> </div> <!-- Sign Out button --> <button class="btn btn-secondary ms-2" id="signOut" onclick="signOut()"> Sign Out </button> </div> </nav> <br /> <div class="container"> <div class="row"> <h5 id="title-div" class="card-header text-center"> JavaScript single-page application secured with MSAL.js </h5> <br /> <h5 id="welcome-div" class="card-header text-center d-none"></h5> <table class="table table-striped table-bordered d-none" id="table-div"> <thead> <tr> <th>Claim Type</th> <th>Value</th> <th>Description</th> </tr> </thead> <tbody id="table-body-div"></tbody> </table> </div> </div> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"> </script> <!-- Bootstrap 5 JS bundle (includes Popper) --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"> </script> <!-- Your custom scripts --> <script type="text/javascript" src="./authConfig.js"></script> <script type="text/javascript" src="./ui.js"></script> <script type="text/javascript" src="./claimUtils.js"></script> <script type="text/javascript" src="./auth.js"></script> </body> </html>
파일을 저장합니다.
ui.js 파일에 코드 추가
애플리케이션을 대화형으로 만들기 위해 ui.js 파일을 사용하여 애플리케이션의 UI 요소를 처리합니다. 파일에는 로그인할 때 사용자의 이름을 업데이트하고 토큰의 클레임으로 테이블을 업데이트하는 데 사용되는 함수가 포함되어 있습니다.
public/ui.js 열고 다음 코드 조각을 추가합니다.
// Select DOM elements to work with const signInButton = document.getElementById('signIn'); const signOutButton = document.getElementById('signOut'); const titleDiv = document.getElementById('title-div'); const welcomeDiv = document.getElementById('welcome-div'); const tableDiv = document.getElementById('table-div'); const tableBody = document.getElementById('table-body-div'); function showWelcomeMessage(account) { signInButton.classList.add('d-none'); signOutButton.classList.remove('d-none'); titleDiv.classList.add('d-none'); welcomeDiv.classList.remove('d-none'); welcomeDiv.innerHTML = `Welcome ${account.username}!`; updateTable(account); }; function updateTable(account) { tableDiv.classList.remove('d-none'); const tokenClaims = createClaimsTable(account.idTokenClaims); Object.keys(tokenClaims).forEach((key) => { let row = tableBody.insertRow(0); let cell1 = row.insertCell(0); let cell2 = row.insertCell(1); let cell3 = row.insertCell(2); cell1.innerHTML = tokenClaims[key][0]; cell2.innerHTML = tokenClaims[key][1]; cell3.innerHTML = tokenClaims[key][2]; }); };
파일을 저장합니다.
signout.html 파일에 코드 추가
signout.html 파일은 애플리케이션에서 로그아웃할 때 사용자에게 메시지를 표시하는 데 사용됩니다.
public/signout.html 열고 다음 코드 조각을 추가합니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Microsoft Entra ID | JavaScript SPA</title> <link rel="SHORTCUT ICON" href="./favicon.svg" type="image/x-icon"> <!-- adding Bootstrap 4 for UI components --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> </head> <body> <div class="jumbotron" style="margin: 10%"> <h1>Goodbye!</h1> <p>You have signed out and your cache has been cleared.</p> <a class="btn btn-primary" href="/" role="button">Take me back</a> </div> </body> </html>
파일을 저장합니다.
앱에 스타일 추가
마지막으로 애플리케이션에 스타일을 추가하여 더 매력적으로 보이게 합니다. 스타일은 styles.css 파일에 추가되며 필요에 맞게 사용자 지정할 수 있습니다.
public/styles.css 열고 다음 코드 조각을 추가합니다.
.navbarStyle { padding: .5rem 1rem !important; } .table-responsive-ms { max-height: 39rem !important; padding-left: 10%; padding-right: 10%; }
파일을 저장합니다.