중요합니다
2025년 5월 1일부터 새 고객을 위해 Azure AD B2C를 더 이상 구매할 수 없습니다. FAQ에서 자세히 알아보세요.
이 문서에서는 자체 SPA(단일 페이지 애플리케이션)에 Azure AD B2C(Azure Active Directory B2C) 인증을 추가하는 방법을 보여 줍니다. JavaScript용 Microsoft 인증 라이브러리(MSAL.js)를 사용하여 SPA 애플리케이션을 만드는 방법을 알아봅니다.
샘플 SPA 애플리케이션에서 인증 구성과 함께 이 문서를 사용하여 사용자 고유의 SPA 앱으로 샘플 SPA 앱을 대체합니다.
개요
이 문서에서는 Node.js 및 Express 를 사용하여 기본 Node.js 웹앱을 만듭니다. Express는 웹 Node.js 모바일 애플리케이션을 위한 일련의 기능을 제공하는 최소한의 유연한 웹 앱 프레임워크입니다.
MSAL.js 인증 라이브러리는 SPA 앱에 인증 및 권한 부여 지원을 간단하게 추가할 수 있도록 Microsoft에서 제공하는 라이브러리입니다.
팁 (조언)
전체 MSAL.js 코드는 클라이언트 측에서 실행됩니다. Node.js 및 Express 서버 측 코드를 .NET Core, Java 및 PHP(Hypertext Preprocessor) 스크립팅 언어와 같은 다른 솔루션으로 대체할 수 있습니다.
필수 조건
필수 구성 요소 및 통합 지침을 검토하려면 샘플 SPA 애플리케이션에서 인증 구성을 참조하세요.
1단계: SPA 앱 프로젝트 만들기
기존 SPA 앱 프로젝트를 사용하거나 새로 만들 수 있습니다. 새 프로젝트를 만들려면 다음을 수행합니다.
명령 셸을 열고 새 디렉터리(예: myApp)를 만듭니다. 이 디렉터리에는 앱 코드, 사용자 인터페이스 및 구성 파일이 포함됩니다.
만든 디렉터리를 입력합니다.
명령
npm init
을 사용하여 앱을 위해package.json
파일을 생성합니다. 이 명령은 앱에 대한 정보(예: 앱의 이름 및 버전, 초기 진입점의 이름, index.js 파일)를 입력하라는 메시지를 표시합니다. 다음 명령을 실행하고 기본값을 적용합니다.
npm init
2단계: 종속성 설치
Express 패키지를 설치하려면 명령 셸에서 다음 명령을 실행합니다.
npm install express
앱의 정적 파일을 찾기 위해 서버 쪽 코드는 경로 패키지를 사용합니다.
Path 패키지를 설치하려면 명령 셸에서 다음 명령을 실행합니다.
npm install path
3단계: 웹 서버 구성
myApp 폴더에서 다음 코드가 포함된 index.js라는 파일을 만듭니다.
// Initialize express
const express = require('express');
const app = express();
// The port to listen to incoming HTTP requests
const port = 6420;
// Initialize path
const path = require('path');
// Set the front-end folder to serve public assets.
app.use(express.static('App'));
// Set up a route for the index.html
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/index.html'));
});
// Start the server, and listen for HTTP requests
app.listen(port, () => {
console.log(`Listening on http://localhost:${port}`);
});
4단계: SPA 사용자 인터페이스 만들기
SPA 앱 index.html
파일을 추가합니다. 이 파일은 부트스트랩 프레임워크를 사용하여 빌드된 사용자 인터페이스를 구현하고 구성, 인증 및 웹 API 호출을 위한 스크립트 파일을 가져옵니다.
index.html 파일에서 참조하는 리소스는 다음 표에 자세히 설명되어 있습니다.
참고 문헌 | 정의 |
---|---|
MSAL.js 라이브러리 | MSAL.js 인증 JavaScript 라이브러리 CDN 경로. |
부트스트랩 스타일시트 | 더 빠르고 쉬운 웹 개발을 위한 무료 프런트 엔드 프레임워크입니다. 프레임워크에는 HTML 기반 및 CSS 기반 디자인 템플릿이 포함됩니다. |
SPA 인덱스 파일을 렌더링하려면 myApp 폴더에서 다음 HTML 코드 조각이 포함된 index.html라는 파일을 만듭니다.
<!DOCTYPE html>
<html>
<head>
<title>My Azure AD B2C test app</title>
</head>
<body>
<h2>My Azure AD B2C test app</h2>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />
<button type="button" id="signIn" class="btn btn-secondary" onclick="signIn()">Sign-in</button>
<button type="button" id="signOut" class="btn btn-success d-none" onclick="signOut()">Sign-out</button>
<h5 id="welcome-div" class="card-header text-center d-none"></h5>
<br />
<!-- Content -->
<div class="card">
<div class="card-body text-center">
<pre id="response" class="card-text"></pre>
<button type="button" id="callApiButton" class="btn btn-primary d-none" onclick="passTokenToApi()">Call API</button>
</div>
</div>
<script src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js" integrity="sha384-ggh+EF1aSqm+Y4yvv2n17KpurNcZTeYtUZUvhPziElsstmIEubyEB6AIVpKLuZgr" crossorigin="anonymous"></script>
<!-- Importing app scripts (load order is important) -->
<script type="text/javascript" src="./apiConfig.js"></script>
<script type="text/javascript" src="./policies.js"></script>
<script type="text/javascript" src="./authConfig.js"></script>
<script type="text/javascript" src="./ui.js"></script>
<!-- <script type="text/javascript" src="./authRedirect.js"></script> -->
<!-- uncomment the above line and comment the line below if you would like to use the redirect flow -->
<script type="text/javascript" src="./authRedirect.js"></script>
<script type="text/javascript" src="./api.js"></script>
</body>
</html>
5단계: 인증 라이브러리 구성
MSAL.js 라이브러리가 Azure AD B2C와 통합되는 방법을 구성합니다. MSAL.js 라이브러리는 공통 구성 개체를 사용하여 Azure AD B2C 테넌트의 인증 엔드포인트에 연결합니다.
인증 라이브러리를 구성하려면 다음을 수행합니다.
myApp 폴더에서 App이라는 새 폴더를 만듭니다.
App 폴더 내에 authConfig.js라는 새 파일을 만듭니다.
authConfig.js 파일에 다음 JavaScript 코드를 추가합니다.
const msalConfig = { auth: { clientId: "<Application-ID>", authority: b2cPolicies.authorities.signUpSignIn.authority, knownAuthorities: [b2cPolicies.authorityDomain], redirectUri: "http://localhost:6420", }, cache: { cacheLocation: "localStorage", . storeAuthStateInCookie: false, } }; const loginRequest = { scopes: ["openid", ...apiConfig.b2cScopes], }; const tokenRequest = { scopes: [...apiConfig.b2cScopes], forceRefresh: false };
<Application-ID>
를 앱 등록 애플리케이션 ID로 교체합니다. 자세한 내용은 샘플 SPA 애플리케이션에서 인증 구성을 참조하세요.
팁 (조언)
MSAL 개체 구성 옵션에 대한 자세한 내용은 인증 옵션 문서를 참조하세요.
6단계: Azure AD B2C 사용자 흐름 지정
Azure AD B2C 환경에 대한 정보를 제공하는 policies.js 파일을 만듭니다. MSAL.js 라이브러리는 이 정보를 사용하여 Azure AD B2C에 대한 인증 요청을 만듭니다.
Azure AD B2C 사용자 흐름을 지정하려면 다음을 수행합니다.
App 폴더 내에 policies.js라는 새 파일을 만듭니다.
policies.js 파일에 다음 코드를 추가합니다.
const b2cPolicies = { names: { signUpSignIn: "B2C_1_SUSI", editProfile: "B2C_1_EditProfile" }, authorities: { signUpSignIn: { authority: "https://contoso.b2clogin.com/contoso.onmicrosoft.com/Your-B2C-SignInOrSignUp-Policy-Id", }, editProfile: { authority: "https://contoso.b2clogin.com/contoso.onmicrosoft.com/Your-B2C-EditProfile-Policy-Id" } }, authorityDomain: "contoso.b2clogin.com" }
"
B2C_1_SUSI
"을 Azure AD B2C 로그인 정책 이름으로 바꾸십시오.B2C_1_EditProfile
을(를) 편집 프로필 Azure AD B2C 정책 이름으로 바꾸십시오.모든 인스턴스를
contoso
Azure AD B2C 테넌트 이름으로 바꿉니다.
7단계: MSAL을 사용하여 사용자 로그인
이 단계에서는 로그인 흐름, API 액세스 토큰 획득 및 로그아웃 메서드를 초기화하는 메서드를 구현합니다.
자세한 내용은 MSAL(Microsoft 인증 라이브러리)을 사용하여 사용자 문서에 로그인하는 방법을 참조하세요.
사용자를 로그인하려면 다음을 수행합니다.
App 폴더 내에 authRedirect.js라는 새 파일을 만듭니다.
authRedirect.js에서 다음 코드를 복사하여 붙여넣습니다.
// Create the main myMSALObj instance // configuration parameters are located at authConfig.js const myMSALObj = new msal.PublicClientApplication(msalConfig); let accountId = ""; let idTokenObject = ""; let accessToken = null; myMSALObj.handleRedirectPromise() .then(response => { if (response) { /** * For the purpose of setting an active account for UI update, we want to consider only the auth response resulting * from SUSI flow. "tfp" claim in the id token tells us the policy (NOTE: legacy policies may use "acr" instead of "tfp"). * To learn more about B2C tokens, visit https://learn.microsoft.com/azure/active-directory-b2c/tokens-overview */ if (response.idTokenClaims['tfp'].toUpperCase() === b2cPolicies.names.signUpSignIn.toUpperCase()) { handleResponse(response); } } }) .catch(error => { console.log(error); }); function setAccount(account) { accountId = account.homeAccountId; idTokenObject = account.idTokenClaims; myClaims= JSON.stringify(idTokenObject); welcomeUser(myClaims); } function selectAccount() { /** * See here for more information 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.length < 1) { return; } else if (currentAccounts.length > 1) { /** * Due to the way MSAL caches account objects, the auth response from initiating a user-flow * is cached as a new account, which results in more than one account in the cache. Here we make * sure we are selecting the account with homeAccountId that contains the sign-up/sign-in user-flow, * as this is the default flow the user initially signed-in with. */ const accounts = currentAccounts.filter(account => account.homeAccountId.toUpperCase().includes(b2cPolicies.names.signUpSignIn.toUpperCase()) && account.idTokenClaims.iss.toUpperCase().includes(b2cPolicies.authorityDomain.toUpperCase()) && account.idTokenClaims.aud === msalConfig.auth.clientId ); if (accounts.length > 1) { // localAccountId identifies the entity for which the token asserts information. if (accounts.every(account => account.localAccountId === accounts[0].localAccountId)) { // All accounts belong to the same user setAccount(accounts[0]); } else { // Multiple users detected. Logout all to be safe. signOut(); }; } else if (accounts.length === 1) { setAccount(accounts[0]); } } else if (currentAccounts.length === 1) { setAccount(currentAccounts[0]); } } // in case of page refresh selectAccount(); async function handleResponse(response) { if (response !== null) { setAccount(response.account); } else { selectAccount(); } } function signIn() { myMSALObj.loginRedirect(loginRequest); } function signOut() { const logoutRequest = { postLogoutRedirectUri: msalConfig.auth.redirectUri, }; myMSALObj.logoutRedirect(logoutRequest); } function getTokenRedirect(request) { request.account = myMSALObj.getAccountByHomeId(accountId); return myMSALObj.acquireTokenSilent(request) .then((response) => { // In case the response from B2C server has an empty accessToken field // throw an error to initiate token acquisition if (!response.accessToken || response.accessToken === "") { throw new msal.InteractionRequiredAuthError; } else { console.log("access_token acquired at: " + new Date().toString()); accessToken = response.accessToken; passTokenToApi(); } }).catch(error => { console.log("Silent token acquisition fails. Acquiring token using popup. \n", error); if (error instanceof msal.InteractionRequiredAuthError) { // fallback to interaction when silent call fails return myMSALObj.acquireTokenRedirect(request); } else { console.log(error); } }); } // Acquires and access token and then passes it to the API call function passTokenToApi() { if (!accessToken) { getTokenRedirect(tokenRequest); } else { try { callApi(apiConfig.webApi, accessToken); } catch(error) { console.log(error); } } } function editProfile() { const editProfileRequest = b2cPolicies.authorities.editProfile; editProfileRequest.loginHint = myMSALObj.getAccountByHomeId(accountId).username; myMSALObj.loginRedirect(editProfileRequest); }
8단계: 웹 API 위치 및 범위 구성
SPA 앱이 웹 API를 호출할 수 있도록 하려면 웹 API 엔드포인트 위치와 웹 API에 대한 액세스 권한을 부여하는 데 사용할 범위를 제공합니다.
웹 API 위치 및 범위를 구성하려면 다음을 수행합니다.
App 폴더 내에 apiConfig.js라는 새 파일을 만듭니다.
apiConfig.js에서 다음 코드를 복사하여 붙여넣습니다.
// The current application coordinates were pre-registered in a B2C tenant. const apiConfig = { b2cScopes: ["https://contoso.onmicrosoft.com/tasks/tasks.read"], webApi: "https://mydomain.azurewebsites.net/tasks" };
contoso
를 해당하는 테넌트 이름으로 바꿉니다. 필요한 범위 이름은 범위 구성 문서에 설명된 대로 찾을 수 있습니다.값을
webApi
웹 API 엔드포인트 위치로 바꿉다.
9단계: 웹 API 호출
API 엔드포인트에 대한 HTTP 요청을 정의합니다. HTTP 요청은 MSAL.js 사용하여 획득한 액세스 토큰을 요청의 HTTP 헤더에 Authorization
전달하도록 구성됩니다.
다음 코드는 HTTP GET
헤더 내에서 액세스 토큰을 전달하여 API 엔드포인트에 Authorization
대한 HTTP 요청을 정의합니다. API 위치는 webApi
의 키에 의해 정의됩니다.
획득한 토큰을 사용하여 웹 API를 호출하려면 다음을 수행합니다.
App 폴더 내에 api.js라는 새 파일을 만듭니다.
api.js 파일에 다음 코드를 추가합니다.
function callApi(endpoint, token) { const headers = new Headers(); const bearer = `Bearer ${token}`; headers.append("Authorization", bearer); const options = { method: "GET", headers: headers }; logMessage('Calling web API...'); fetch(endpoint, options) .then(response => response.json()) .then(response => { if (response) { logMessage('Web API responded: ' + response.name); } return response; }).catch(error => { console.error(error); }); }
10단계: UI 요소 참조 추가
SPA 앱은 JavaScript를 사용하여 UI 요소를 제어합니다. 예를 들어 로그인 및 로그아웃 단추를 표시하고 사용자의 ID 토큰 클레임을 화면에 렌더링합니다.
UI 요소 참조를 추가하려면 다음을 수행합니다.
App 폴더 내에 ui.js라는 파일을 만드세요.
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'); const editProfileButton = document.getElementById('editProfileButton'); const callApiButton = document.getElementById('callApiButton'); const response = document.getElementById("response"); const label = document.getElementById('label'); function welcomeUser(claims) { welcomeDiv.innerHTML = `Token claims: </br></br> ${claims}!` signInButton.classList.add('d-none'); signOutButton.classList.remove('d-none'); welcomeDiv.classList.remove('d-none'); callApiButton.classList.remove('d-none'); } function logMessage(s) { response.appendChild(document.createTextNode('\n' + s + '\n')); }
11단계: SPA 애플리케이션 실행
명령 셸에서 다음 명령을 실행합니다.
npm install
npm ./index.js
- https://localhost:6420.으로 이동
- 로그인을 선택합니다.
- 등록 또는 로그인 프로세스를 완료합니다.
성공적으로 인증되면 구문 분석된 ID 토큰이 화면에 표시됩니다. API 엔드포인트를 호출하려면 선택합니다 Call API
.
다음 단계
- 코드 샘플에 대해 자세히 알아봅니다.
- Azure AD B2C를 사용하여 자체 SPA 애플리케이션에서 인증 옵션을 구성합니다.
- 고유 웹 API에서 인증을 사용하도록 설정