단일 페이지 앱
IBM Cloud® App ID를 사용하면 권한 부여 및 인증을 단일 페이지 애플리케이션(SPA)에 신속하게 추가할 수 있습니다. SPA는 브라우저에서 완전히 실행되며, 관리하는 백엔드가 없고, 애플리케이션을 사용하는 중에 페이지를 다시 로드할 필요가 없습니다. SPA와 함께 사용되는 일부 공통 프레임워크는 Anangular 및 React입니다.
앱에 사용자가 제어하는 백엔드가 있습니까? SPA는 사용자의 플로우가 아닙니다. 웹 앱 플로우를 시도하십시오!
플로우에 대한 정보
현재 SPA에 대한 업계 표준이지만 OAuth 워킹 그룹에서는 몇 가지 보안 문제로 인해 더 이상 암시적 흐름 사용을 권장하지 않습니다. 이들의 조언에 따라 App ID 은 PKCE와 함께 인증 코드 플로우를 사용하도록 구성되었습니다.
- 사용자가 단일 페이지 애플리케이션에 로그인하려고 합니다.
- App ID SDK에서는 코드 인증 확인의 일반 텍스트 버전인 권한 부여 요청의 코드 검증기를 작성합니다. 클라이언트에서는 권한 부여 요청과 함께 인증 확인을 인코딩하는 데 사용하는 인증 확인 메소드와 코드 인증 확인을 보냅니다.
- App ID가 새 창에서 인증 플로우를 시작합니다.
- 사용자가 인증할 ID 제공자를 선택하고 사인인 프로세스를 완료합니다.
- 애플리케이션의 App ID SDK가 권한 부여 코드를 받습니다.
- 그러면 SDK에서 액세스 및 ID 토큰을 얻기 위해 권한 부여 코드 및 코드 검증기와 함께 App ID 토큰 엔드포인트에 XHR을 요청합니다.
클라우드 디렉토리에 SSO를 사용 중입니까? App ID 클라이언트 SDK를 사용하면 사용자가 명시적으로 로그인할 필요 없이 자동으로 새 토큰 쌍을 얻을 수 있습니다. 자세한 정보는 자동 로그인을 참조하십시오.
권한 코드 + PKCE를 사용하는 이유
단일 페이지 애플리케이션의 특성 때문에 시크릿을 안전하게 저장할 수 없습니다. OAuth 2.0 권한 코드 플로우의 변형인 권한 부여 + PKCE 플로우에서는 시크릿이 아니라 일회성 코드 검증기와 인증 확인을 사용하여 문제를 해결합니다. 검증기 및 인증 확인은 권한 및 토큰 엔드포인트를 호출하는 엔티티가 동일한지 확인합니다. 이 프로세스는 공격자가 코드 검증기를 인식하지 않고 권한 부여 서버에서 토큰을 요청할 수 없음을 의미합니다.
내재적 플로우가 현재 업계 표준이지만 여러 가지 보안상의 결점이 있습니다. 예를 들어 내재적 플로우에서는 경로 재지정 URI를 사용하여 토큰을 얻습니다. 이 URI에서는 XHR 요청을 대신 사용하여 권한 + PKCE 플로우를 방지합니다. 보안 결함 때문에 내재적 플로우는 더 이상 권장되지 않거나 다음과 같은 이유 때문에 안전한 것으로 간주되지 않습니다.
-
내재적 플로우에서는 URL의 일부(조회 매개변수 또는 해시 단편)로 토큰을 리턴합니다. 그러면 토큰을 인터셉트하여 토큰에 액세스할 수 있습니다. 토큰은 사용자의 브라우저 히스토리 또는 로그에 저장할 수 있습니다. 히스토리 또는 로그는 클라우드 서비스에 저장하고 여러 디바이스에 보낼 수 있으므로, 역시 위험이 높아집니다.
-
암시적 흐름은 리디렉션 URI 공격에 취약하므로 공격자가 승인된 리디렉션 URI를 원하는 대상으로 대체할 수 있습니다. 경로 재지정 URI 공격이 있으면 사용자가 변경된 링크를 따라 이동하여 클라이언트에 권한을 부여합니다. 권한을 부여하고 나면 공격자 URI로 경로가 재지정됩니다. 이 URI에서는 공격자가 사용자의 적법한 토큰에 액세스하는 권한을 부여합니다.
앞의 예제는 중요한 문제 중 단 몇 가지에 지나지 않습니다. 자세한 내용은 OAuth 2.0 보안 모범 사례를 참조하세요.
시작하기 전에
시작하기 전에 다음 전제조건이 있는지 확인하십시오.
- App ID 서비스 인스턴스.
- App ID 서비스 대시보드의 경로 재지정 URI 세트.
- 단일 페이지 애플리케이션. 애플리케이션이 없으며 플로우를 시도해 보려면 App ID 대시보드의 개요 페이지에서 동일한 애플리케이션을 다운로드하십시오.
GUI를 사용하여 애플리케이션 인증 정보 작성
인증 정보는 App ID에 애플리케이션을 연결하는 데 사용됩니다. 인증 정보를 작성하려면 App ID에 앱을 등록하십시오.
SPA 인증 정보의 클라이언트 시크릿은 리턴되지 않습니다. 권한 부여 + PKCE 플로우의 시크릿은 필요하지 않습니다. SPA 플로우에서는 일반 웹 앱 인증 정보를 사용할 수 없습니다.
- 작업할 지역의 IBM Cloud 대시보드에 로그인하십시오.
- 애플리케이션 탭으로 이동하여 애플리케이션 추가를 클릭하십시오.
- 애플리케이션에 이름을 지정하십시오.
- 유형 드롭 다운에서 단일 페이지 애플리케이션을 선택하십시오.
- 저장 을 클릭하십시오.
- 테이블에서 인증 정보 보기를 클릭하여 다음 구성에 필요한 정보를 확인하십시오.
API를 사용하여 애플리케이션 인증 정보 작성
인증 정보는 App ID에 애플리케이션을 연결하는 데 사용됩니다. 인증 정보를 작성하려면 App ID에 앱을 등록하십시오.
SPA 인증 정보의 클라이언트 시크릿은 리턴되지 않습니다. 권한 부여 + PKCE 플로우의 시크릿은 필요하지 않습니다. SPA 플로우에서는 일반 웹 앱 인증 정보를 사용할 수 없습니다.
-
다음 게시물 요청을 하세요 /management/v4/
/applications 엔드포인트.curl -X POST \ https://us-south.appid.cloud.ibm.com/management/v4/<tenantID>/applications/ \ -H 'accept: application/json' \ -H 'Authorization: Bearer <IAMToken>' \ -H 'Content-Type: application/json' \ -d '{"name": "MySampleSPA", "type": "singlepageapp"}'리소스 예:
{ "clientId": "<clientID>", "tenantId": "<tenantID>", "name": "MySampleSPA", "oAuthServerUrl": "https://us-south.appid.cloud.ibm.com/oauth/v4/<tenantID>", "type": "singlepageapp" }
JavaScript SDK 구성
애플리케이션에 SDK를 설치하려면 다음 단계를 안내서로 사용하십시오.
-
명령 프롬프트를 사용하여 애플리케이션이 포함된 디렉토리로 변경하십시오.
-
NPM을 사용하거나 기본 HTML 파일에서 CDN을 링크하여 App ID 서비스를 설치하십시오.
-
NPM을 사용하려면 다음 명령을 실행하십시오.
npm install ibmcloud-appid-js -
CDN을 추가하려면 기본 HTML 파일에 다음 링크를 추가하십시오.
<script src="https://cdn.appid.cloud.ibm.com/appid-0.3.0.min.js"></script>
-
-
클라이언트 ID와 발견 엔드포인트를 앱에 추가하여 SDK를 초기화하십시오.
const appID = new AppID(); await appID.init({ clientId: '<spaClientID>', discoveryEndpoint: '<wellKnownEndpoint>' }); -
애플리케이션 코드에서 로그인 단추를 구성한 후
signin에 호출을 추가하십시오. 사용자에게 인증 정보를 입력하도록 프롬프트하는 팝업 창이 열립니다. 인증이 성공적으로 완료되면 화면이 닫히고 인증됩니다.const tokens = await appID.signin();
자동 로그인 구성
클라우드 디렉토리용 SSO를 사용하면 자동 로그인을 사용하여 다시 인증하지 않아도 사용자의 새 토큰을 자동으로 얻을 수 있습니다. 자동 로그인을 사용하려면 다음 단계를 안내서로 사용하십시오.
새로 고치기 토큰은 SPA 플로우에서 리턴되지 않습니다.
-
App ID 대시보드에서 클라우드 디렉토리 > 싱글 사인온으로 이동하십시오.
-
싱글 사인온 사용을 사용으로 전환하십시오.
-
저장 을 클릭하십시오.
-
애플리케이션에 다음 코드를 추가하십시오. 자동 로그인에 실패하면 로그인 단추를 표시하도록 애플리케이션을 구성하십시오.
const tokens = await appID.silentSignin(); if (!tokens) { document.getElementById('login').addEventListener('click', async () => { const tokens = await appID.signin(); }); }