다음을 통해 공유


자습서: JavaScript SPA(단일 페이지 앱)에서 로그인 및 로그아웃

적용 대상: 직원 테넌트에 다음 내용이 적용되었음을 나타내는 흰색 확인 표시 기호가 있는 녹색 원입니다. 다음 콘텐츠가 외부 테넌트에 적용되었음을 나타내는 흰색 확인 표시 기호가 있는 Workforce 테넌트 녹색 원입니다. 외부 테넌트(자세한 정보)

이 자습서는 JavaScript SPA(단일 페이지 애플리케이션)를 빌드하고 Microsoft ID 플랫폼을 사용하여 인증을 준비하는 방법을 보여 주는 시리즈의 마지막 부분입니다. 이 시리즈 2부에서는 JavaScript SPA에 인증 흐름을 추가하고 반응형 UI를 빌드했습니다. 이 마지막 단계에서는 앱에서 로그인 및 로그아웃 기능을 테스트하는 방법을 보여 줍니다.

이 자습서에서는 다음을 수행합니다.

  • claimUtils.js 파일에 코드를 추가하여 클레임 테이블 만들기
  • 애플리케이션 로그인 및 로그아웃
  • ID 토큰에서 반환된 클레임 보기

필수 구성 요소

claimUtils.js 파일에 코드 추가(선택 사항)

ID 토큰에서 반환된 클레임을 표시할 수 있는 테이블의 기능을 추가하려면 claimUtils.js 파일에 코드를 추가할 수 있습니다. 이 코드 조각은 적절한 설명과 해당 값으로 클레임 테이블을 채웁다.

  1. public/claimUtils.js 열고 다음 코드 조각을 추가합니다.

     /**
     * Populate claims table with appropriate description
     * @param {Object} claims ID token claims
     * @returns claimsObject
     */
    const createClaimsTable = (claims) => {
        let claimsObj = {};
        let index = 0;
    
        Object.keys(claims).forEach((key) => {
            if (typeof claims[key] !== 'string' && typeof claims[key] !== 'number') return;
            switch (key) {
                case 'aud':
                    populateClaim(
                        key,
                        claims[key],
                        "Identifies the intended recipient of the token. In ID tokens, the audience is your app's Application ID, assigned to your app in the Entra admin center.",
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'iss':
                    populateClaim(
                        key,
                        claims[key],
                        'Identifies the issuer, or authorization server that constructs and returns the token. It also identifies the Microsoft Entra tenant for which the user was authenticated. If the token was issued by the v2.0 endpoint, the URI will end in /v2.0. The GUID that indicates that the user is a consumer user from a Microsoft account is 9188040d-6c67-4c5b-b112-36a304b66dad.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'iat':
                    populateClaim(
                        key,
                        changeDateFormat(claims[key]),
                        'Issued At indicates when the authentication for this token occurred.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'nbf':
                    populateClaim(
                        key,
                        changeDateFormat(claims[key]),
                        'The nbf (not before) claim identifies the time (as UNIX timestamp) before which the JWT must not be accepted for processing.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'exp':
                    populateClaim(
                        key,
                        changeDateFormat(claims[key]),
                        "The exp (expiration time) claim identifies the expiration time (as UNIX timestamp) on or after which the JWT must not be accepted for processing. It's important to note that in certain circumstances, a resource may reject the token before this time. For example, if a change in authentication is required or a token revocation has been detected.",
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'name':
                    populateClaim(
                        key,
                        claims[key],
                        "The principal about which the token asserts information, such as the user of an application. This value is immutable and can't be reassigned or reused. It can be used to perform authorization checks safely, such as when the token is used to access a resource. By default, the subject claim is populated with the object ID of the user in the directory",
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'preferred_username':
                    populateClaim(
                        key,
                        claims[key],
                        'The primary username that represents the user. It could be an email address, phone number, or a generic username without a specified format. Its value is mutable and might change over time. Since it is mutable, this value must not be used to make authorization decisions. It can be used for username hints, however, and in human-readable UI as a username. The profile scope is required in order to receive this claim.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'nonce':
                    populateClaim(
                        key,
                        claims[key],
                        'The nonce matches the parameter included in the original /authorize request to the IDP. If it does not match, your application should reject the token.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'oid':
                    populateClaim(
                        key,
                        claims[key],
                        'The oid (user’s object id) is the only claim that should be used to uniquely identify a user in an Azure AD tenant. The token might have one or more of the following claim, that might seem like a unique identifier, but is not and should not be used as such.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'tid':
                    populateClaim(
                        key,
                        claims[key],
                        'The tenant ID. You will use this claim to ensure that only users from the current Azure AD tenant can access this app.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'upn':
                    populateClaim(
                        key,
                        claims[key],
                        '(user principal name) – might be unique amongst the active set of users in a tenant but tend to get reassigned to new employees as employees leave the organization and others take their place or might change to reflect a personal change like marriage.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'email':
                    populateClaim(
                        key,
                        claims[key],
                        'Email might be unique amongst the active set of users in a tenant but tend to get reassigned to new employees as employees leave the organization and others take their place.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'acct':
                    populateClaim(
                        key,
                        claims[key],
                        'Available as an optional claim, it lets you know what the type of user (homed, guest) is. For example, for an individual’s access to their data you might not care for this claim, but you would use this along with tenant id (tid) to control access to say a company-wide dashboard to just employees (homed users) and not contractors (guest users).',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'sid':
                    populateClaim(key, claims[key], 'Session ID, used for per-session user sign-out.', index, claimsObj);
                    index++;
                    break;
                case 'sub':
                    populateClaim(
                        key,
                        claims[key],
                        'The sub claim is a pairwise identifier - it is unique to a particular application ID. If a single user signs into two different apps using two different client IDs, those apps will receive two different values for the subject claim.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'ver':
                    populateClaim(
                        key,
                        claims[key],
                        'Version of the token issued by the Microsoft identity platform',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'auth_time':
                    populateClaim(
                        key,
                        claims[key],
                        'The time at which a user last entered credentials, represented in epoch time. There is no discrimination between that authentication being a fresh sign-in, a single sign-on (SSO) session, or another sign-in type.',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'at_hash':
                    populateClaim(
                        key,
                        claims[key],
                        'An access token hash included in an ID token only when the token is issued together with an OAuth 2.0 access token. An access token hash can be used to validate the authenticity of an access token',
                        index,
                        claimsObj
                    );
                    index++;
                    break;
                case 'uti':
                case 'rh':
                    index++;
                    break;
                default:
                    populateClaim(key, claims[key], '', index, claimsObj);
                    index++;
            }
        });
    
        return claimsObj;
    };
    
        /**
         * Populates claim, description, and value into an claimsObject
         * @param {string} claim
         * @param {string} value
         * @param {string} description
         * @param {number} index
         * @param {Object} claimsObject
         */
        const populateClaim = (claim, value, description, index, claimsObject) => {
            let claimsArray = [];
            claimsArray[0] = claim;
            claimsArray[1] = value;
            claimsArray[2] = description;
            claimsObject[index] = claimsArray;
        };
    
        /**
         * Transforms Unix timestamp to date and returns a string value of that date
         * @param {string} date Unix timestamp
         * @returns
         */
        const changeDateFormat = (date) => {
            let dateObj = new Date(date * 1000);
            return `${date} - [${dateObj.toString()}]`;
    };
    
  2. 파일을 저장합니다.

프로젝트 실행 및 로그인

이제 필요한 모든 코드 조각이 추가되었으므로 웹 브라우저에서 애플리케이션을 호출하고 테스트할 수 있습니다.

  1. 새 터미널을 열고 다음 명령을 실행하여 Express 웹 서버를 시작합니다.

    npm start
    
  2. 터미널에 표시되는 http URL(예: http://localhost:3000)을 복사하여 브라우저에 붙여넣습니다. 프라이빗 또는 시크릿 브라우저 세션을 사용하는 것이 좋습니다.

  3. 테넌트에 등록된 계정으로 로그인합니다.

  4. 애플리케이션에 로그인했음을 나타내는 다음 스크린샷과 유사한 인터페이스가 나타납니다. 클레임 테이블을 추가한 경우 ID 토큰에서 반환된 클레임을 볼 수 있습니다.

    API 호출 결과를 보여 주는 JavaScript 앱의 스크린샷

애플리케이션에서 로그아웃

  1. 페이지에서 로그아웃 단추를 찾아 선택합니다.
  2. 로그아웃할 계정을 선택하라는 메시지가 표시됩니다. 로그인하는 데 사용한 계정을 선택합니다.

로그아웃했음을 나타내는 메시지가 나타납니다. 이제 브라우저 창을 닫을 수 있습니다.