자습서: Node.js 디먼 애플리케이션에서 웹 API 호출
이 자습서는 OAuth(Open Authorization) 2.0 클라이언트 자격 증명 권한 부여 흐름을 사용하여 Node.js 디먼 클라이언트 앱을 준비한 다음, 웹 API 호출을 위한 액세스 토큰을 획득하도록 구성하는 방법을 보여 주는 시리즈의 마지막 부분입니다. 이 시리즈의 1부에서는 Microsoft Entra 관리 센터에 웹 API 및 디먼 앱을 등록하고 권한을 부여했습니다. 이 마지막 단계에서는 노드용 MSAL(Microsoft 인증 라이브러리)을 사용하여 Node.js 애플리케이션을 빌드하여 앱에 권한 부여를 간소화하는 방법을 보여 줍니다.
이 자습서에서는 다음을 수행합니다.
- Visual Studio Code에서 Node.js 앱을 만든 다음, 종속성을 설치합니다.
- Node.js 앱을 사용하도록 설정하여 웹 API를 호출하기 위한 액세스 토큰을 획득합니다.
필수 조건
- 자습서: Node.js 디먼 애플리케이션에 권한을 부여하기 위해 외부 테넌트를 준비합니다.
- 실행 중이며 요청을 수락할 준비가 된 보호된 웹 API입니다. 아직 만들지 않은 경우 보호된 웹 API 만들기 자습서를 참조하세요. 이 웹 API가 테넌트 준비 자습서에서 만든 앱 등록 세부 정보를 사용하고 있는지 확인합니다. 웹 API가 HTTPS를 통해 다음 엔드포인트를 노출하는지 확인합니다.
GET /api/todolist
은(는) 모든 할 일을 가져옵니다.POST /api/todolist
는 TODO를 추가합니다.
- Node.js
- React 애플리케이션을 지원하는 모든 IDE(통합 개발 환경)를 사용할 수 있지만 이 자습서에서는 Visual Studio Code를 사용합니다.
- 테넌트 준비 자습서에서 만든 Node.js 디먼 앱 및 웹 API에 대한 등록 세부 정보입니다.
Node.js 디먼 프로젝트 만들기
ciam-call-api-node-daemon
과 같은 Node.js 디먼 애플리케이션을 호스팅할 폴더를 만듭니다.
터미널에서 디렉터리를
cd ciam-call-api-node-daemon
과 같은 노드 디먼 앱 폴더로 변경하고npm init -y
를 실행합니다. 이 명령은 Node.js 프로젝트에 대한 기본 package.json 파일을 만듭니다. 이 명령은 Node.js 프로젝트에 대한 기본package.json
파일을 만듭니다.다음 프로젝트 구조를 구현하려면 추가 폴더와 파일을 만듭니다.
ciam-call-api-node-daemon/ ├── auth.js └── authConfig.js └── fetch.js └── index.js └── package.json
앱 종속성 업그레이드
터미널에서 다음 명령을 실행하여 axios
, yargs
, @azure/msal-node
패키지를 설치합니다.
npm install axios yargs @azure/msal-node
MSAL 구성 개체 만들기
코드 편집기에서 authConfig.js 파일을 연 다음, 다음 코드를 추가합니다.
require('dotenv').config();
/**
* Configuration object to be passed to MSAL instance on creation.
* For a full list of MSAL Node configuration parameters, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
*/
const msalConfig = {
auth: {
clientId: process.env.CLIENT_ID || 'Enter_the_Application_Id_Here', // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
authority: process.env.AUTHORITY || 'https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/', // Replace "Enter_the_Tenant_Subdomain_Here" with your tenant subdomain
clientSecret: process.env.CLIENT_SECRET || 'Enter_the_Client_Secret_Here', // Client secret generated from the app
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: 'Info',
},
},
};
const protectedResources = {
apiToDoList: {
endpoint: process.env.API_ENDPOINT || 'https://localhost:44351/api/todolist',
scopes: [process.env.SCOPES || 'api://Enter_the_Web_Api_Application_Id_Here'],
},
};
module.exports = {
msalConfig,
protectedResources,
};
msalConfig
개체에는 권한 부여 흐름의 동작을 사용자 지정하는 데 사용하는 구성 옵션 집합이 포함되어 있습니다.
authConfig.js 파일에서 다음을 대체합니다.
Enter_the_Application_Id_Here
를 이전에 등록한 디먼 앱의 애플리케이션(클라이언트) ID로.Enter_the_Tenant_Subdomain_Here
를 디렉터리(테넌트) 하위 도메인으로 바꿉니다. 예를 들어, 테넌트 기본 도메인이contoso.onmicrosoft.com
인 경우contoso
를 사용합니다. 테넌트 이름이 없는 경우 테넌트 세부 정보를 읽는 방법을 알아봅니다.Enter_the_Client_Secret_Here
를 이전에 복사한 클라이언트 디먼 앱 비밀 값으로.Enter_the_Web_Api_Application_Id_Here
를 이전에 복사한 웹 API 앱의 애플리케이션(클라이언트) ID로.
protectedResources
변수의 scopes
속성은 이전에 등록한 웹 API의 리소스 식별자(애플리케이션 ID URI)입니다. 전체 범위 URI는 api://Enter_the_Web_Api_Application_Id_Here/.default
와 유사합니다.
액세스 토큰 획득
코드 편집기에서 auth.js 파일을 연 다음, 다음 코드를 추가합니다.
const msal = require('@azure/msal-node');
const { msalConfig, protectedResources } = require('./authConfig');
/**
* With client credentials flows permissions need to be granted in the portal by a tenant administrator.
* The scope is always in the format '<resource-appId-uri>/.default'. For more, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
*/
const tokenRequest = {
scopes: [`${protectedResources.apiToDoList.scopes}/.default`],
};
const apiConfig = {
uri: protectedResources.apiToDoList.endpoint,
};
/**
* Initialize a confidential client application. For more info, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/initialize-confidential-client-application.md
*/
const cca = new msal.ConfidentialClientApplication(msalConfig);
/**
* Acquires token with client credentials.
* @param {object} tokenRequest
*/
async function getToken(tokenRequest) {
return await cca.acquireTokenByClientCredential(tokenRequest);
}
module.exports = {
apiConfig: apiConfig,
tokenRequest: tokenRequest,
getToken: getToken,
};
코드에서:
tokenRequest
및apiConfig
개체를 준비합니다.tokenRequest
는 액세스 토큰을 요청하는 범위를 포함합니다. 범위는api://Enter_the_Web_Api_Application_Id_Here/.default
와 같습니다.apiConfig
개체에는 웹 API에 대한 엔드포인트가 포함됩니다. OAuth 2.0 클라이언트 자격 증명 흐름에 대해 자세히 알아봅니다.msalConfig
개체를 ConfidentialClientApplication 클래스의 생성자에 전달하여 기밀 클라이언트 인스턴스를 만듭니다.const cca = new msal.ConfidentialClientApplication(msalConfig);
그런 다음 acquireTokenByClientCredential 함수를 사용하여 액세스 토큰을 획득합니다.
getToken
함수에서 이 논리를 구현합니다.cca.acquireTokenByClientCredential(tokenRequest);
액세스 토큰을 획득하면 API 호출을 계속할 수 있습니다.
API 호출
코드 편집기에서 auth.js 파일을 연 다음, 다음 코드를 추가합니다.
const axios = require('axios');
/**
* Calls the endpoint with authorization bearer token.
* @param {string} endpoint
* @param {string} accessToken
*/
async function callApi(endpoint, accessToken) {
const options = {
headers: {
Authorization: `Bearer ${accessToken}`
}
};
console.log('request made to web API at: ' + new Date().toString());
try {
const response = await axios.get(endpoint, options);
return response.data;
} catch (error) {
console.log(error)
return error;
}
};
module.exports = {
callApi: callApi
};
이 코드에서는 요청 Authorization
헤더에 전달자 토큰으로 액세스 토큰을 전달하여 웹 API를 호출합니다.
Authorization: `Bearer ${accessToken}`
이전에 액세스 토큰 획득에서 획득한 액세스 토큰을 사용합니다.
웹 API가 요청을 수신하면 웹 API는 요청을 평가한 다음 애플리케이션 요청임을 확인합니다. 액세스 토큰이 유효한 경우 웹 API는 요청된 데이터를 반환합니다. 그렇지 않으면 API는 401 Unauthorized
HTTP 오류를 반환합니다.
디먼 앱 완료
코드 편집기에서 index.js 파일을 연 다음, 다음 코드를 추가합니다.
#!/usr/bin/env node
// read in env settings
require('dotenv').config();
const yargs = require('yargs');
const fetch = require('./fetch');
const auth = require('./auth');
const options = yargs
.usage('Usage: --op <operation_name>')
.option('op', { alias: 'operation', describe: 'operation name', type: 'string', demandOption: true })
.argv;
async function main() {
console.log(`You have selected: ${options.op}`);
switch (yargs.argv['op']) {
case 'getToDos':
try {
const authResponse = await auth.getToken(auth.tokenRequest);
const todos = await fetch.callApi(auth.apiConfig.uri, authResponse.accessToken);
} catch (error) {
console.log(error);
}
break;
default:
console.log('Select an operation first');
break;
}
};
main();
이 코드는 앱의 진입점입니다. Node.js 앱에 대해 yargs JavaScript 명령줄 인수 구문 분석 라이브러리를 사용하여 액세스 토큰을 대화형으로 가져온 다음, API를 호출합니다. 앞에서 정의한 getToken
및 callApi
함수를 사용합니다.
const authResponse = await auth.getToken(auth.tokenRequest);
const todos = await fetch.callApi(auth.apiConfig.uri, authResponse.accessToken);
디먼 앱 및 API 실행 및 테스트
이제 클라이언트 디먼 앱 및 웹 API를 테스트할 준비가 되었습니다.
ASP.NET 웹 API 보안 자습서에서 배운 단계를 사용하여 웹 API를 시작합니다. 이제 웹 API가 클라이언트 요청을 처리할 준비가 되었습니다. authConfig.js 파일에 지정된 대로 포트
44351
에서 웹 API를 실행하지 않는 경우 올바른 웹 API의 포트 번호를 사용하도록 authConfig.js 파일을 업데이트해야 합니다.터미널에서
ciam-call-api-node-daemon
과 같은 디먼 Node.js 앱이 포함된 프로젝트 폴더에 있는지 확인한 다음, 다음 명령을 실행합니다.node . --op getToDos
디먼 앱과 웹 API가 성공적으로 실행되면 콘솔 창에서 다음 JSON 배열과 유사한 웹 API 엔드포인트 todos
변수에서 반환된 데이터를 찾아야 합니다.
{
id: 1,
owner: '3e8....-db63-43a2-a767-5d7db...',
description: 'Pick up grocery'
},
{
id: 2,
owner: 'c3cc....-c4ec-4531-a197-cb919ed.....',
description: 'Finish invoice report'
},
{
id: 3,
owner: 'a35e....-3b8a-4632-8c4f-ffb840d.....',
description: 'Water plants'
}