Protected App — SDK 없는 인증 (Authentication) 통합
Protected App은 SDK 통합의 복잡성을 제거하고, 인증 (Authentication) 계층을 애플리케이션과 분리하도록 설계되었습니다. 인증 (Authentication)은 저희가 처리하므로, 여러분은 핵심 기능에 집중할 수 있습니다. 사용자가 인증 (Authentication)되면 Protected App이 서버의 콘텐츠를 제공합니다.
Protected App의 동작 방식
Cloudflare 기반의 Protected App은 글로벌 엣지 네트워크에서 동작하여, 애플리케이션에 낮은 지연 시간과 높은 가용성을 보장합니다.
Protected App은 세션 상태와 사용자 정보를 유지합니다. 사용자가 인증 (Authentication)되지 않은 경우, Protected App은 로그인 페이지로 리디렉션합니다. 인증 (Authentication) 후, Protected App은 사용자의 요청에 인증 (Authentication) 및 사용자 정보를 래핑하여 원본 서버로 전달합니다.
이 과정은 다음 플로우차트로 시각화할 수 있습니다:
원본 서버 보호하기
원본 서버는 Logto의 Protected App이 소유하지 않은 물리적 또는 가상 장치일 수 있으며, 애플리케이션 콘텐츠가 위치합니다. CDN 서버와 유사하게, Protected App은 인증 (Authentication) 과정을 관리하고 원본 서버에서 콘텐츠를 가져옵니다. 따라서 사용자가 원본 서버에 직접 접근할 수 있다면 인증 (Authentication)을 우회할 수 있으므로, 애플리케이션이 더 이상 보호되지 않습니다.
따라서 원본 연결을 안전하게 보호하는 것이 중요합니다. 이는 공격자가 인증 (Authentication) 없이 원본 서버를 발견하고 접근하는 것을 방지합니다. 이를 위한 방법은 다음과 같습니다:
- HTTP 헤더 검증
- JSON Web Token (JWT) 검증
HTTP 헤더 검증
원본 서버를 보호하는 한 가지 방법은 HTTP Basic 인증 (Authentication)을 사용하는 것입니다.
Protected App에서 오는 각 요청에는 다음과 같은 헤더가 포함됩니다:
Authorization: Basic base64(appId:appSecret)
이 헤더를 검증함으로써 요청이 Protected App에서 왔음을 확인하고, 이 헤더가 없는 요청은 거부할 수 있습니다.
Nginx 또는 Apache를 사용하는 경우, 원본 서버에서 HTTP Basic 인증 (Authentication)을 구현하는 방법은 다음 가이드를 참고하세요:
애플리케이션 내에서 헤더를 확인하려면, Cloudflare에서 제공하는 HTTP Basic 인증 (Authentication) 예제를 참고하여 HTTP Basic 스키마로 접근을 제한하는 방법을 배울 수 있습니다.
JSON Web Token (JWT) 검증
원본 서버를 보호하는 또 다른 방법은 JSON Web Token (JWT)을 사용하는 것입니다.
Protected App에서 인증 (Authentication)된 각 요청에는 다음과 같은 헤더가 포함됩니다:
Logto-ID-Token: <JWT>
이 JWT는 ID 토큰 (ID token)으로, Logto에서 서명되며 사용자 정보를 포함합니다. 이 JWT를 검증함으로써 요청이 Protected App에서 왔음을 확인하고, 이 헤더가 없는 요청은 거부할 수 있습니다.
토큰은 JWS 토큰으로 암호화 및 서명됩니다.
검증 단계:
- JWT 검증하기
- JWS 서명 검증하기
- 토큰의 발급자 (Issuer)는
https://<your-logto-domain>/oidc(여러분의 Logto 인증 (Authentication) 서버에서 발급)
const express = require('express');
const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');
const ISSUER = 'https://<your-logto-domain>/oidc';
const CERTS_URL = 'https://<your-logto-domain>/oidc/jwks';
const client = jwksClient({
jwksUri: CERTS_URL,
});
const getKey = (header, callback) => {
client.getSigningKey(header.kid, function (err, key) {
callback(err, key?.getPublicKey());
});
};
const verifyToken = (req, res, next) => {
const token = req.headers['Logto-ID-Token'];
// 들어오는 요청에 토큰 헤더가 있는지 확인
if (!token) {
return res.status(403).send({ status: false, message: '필수 Logto-ID-Token 헤더가 없습니다.' });
}
jwt.verify(token, getKey, { issuer: ISSUER }, (err, decoded) => {
if (err) {
return res.status(403).send({ status: false, message: '유효하지 않은 ID 토큰입니다.' });
}
req.user = decoded;
next();
});
};
const app = express();
app.use(verifyToken);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000);
인증 (Authentication) 상태 및 사용자 정보 가져오기
애플리케이션에서 인증 (Authentication) 및 사용자 정보를 가져와야 하는 경우, Logto-ID-Token 헤더를 사용할 수 있습니다.
토큰을 단순히 디코딩만 하고 싶다면, 다음 코드를 사용할 수 있습니다:
const express = require('express');
const decodeIdToken = (req, res, next) => {
const token = req.headers['Logto-ID-Token'];
if (!token) {
return res.status(403).send({
status: false,
message: '필수 Logto-ID-Token 헤더가 없습니다.',
});
}
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('유효하지 않은 ID 토큰입니다.');
}
const payload = parts[1];
const decodedPayload = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
const claims = JSON.parse(decodedPayload);
req.user = claims;
next();
};
const app = express();
app.use(decodeIdToken);
app.get('/', (req, res) => {
res.json(req.user);
});
app.listen(3000);
ID 토큰 (ID token) 클레임 (Claim) 커스터마이즈하기
기본적으로, Logto-ID-Token 헤더에는 표준 OIDC 클레임 (예: sub, name, email)이 포함됩니다. 역할이나 조직 데이터와 같은 확장 클레임을 포함하려면, 다음 두 가지를 모두 설정해야 합니다:
- 테넌트 토글: Console > Custom JWT > ID token에서 클레임을 활성화하세요.
- Protected App 스코프: Protected App 설정에서 ID 토큰 (ID token) 클레임 > 추가 스코프에서 해당 스코프를 선택하세요.
확장 클레임은 Custom JWT에서 클레임이 활성화되고, Protected App에 해당 스코프가 선택된 경우에만 전달된 ID 토큰 (ID token)에 포함됩니다. 전체 확장 스코프 및 클레임 목록은 Custom ID token을 참고하세요.
| Scope | Claims |
|---|---|
custom_data | custom_data |
identities | identities, sso_identities |
roles | roles |
urn:logto:scope:organizations | organizations, organization_data |
urn:logto:scope:organization_roles | organization_roles |
원본 호스트 가져오기
클라이언트가 요청한 원본 호스트가 필요하다면, Logto-Host 또는 x-forwarded-host 헤더를 사용할 수 있습니다.
인증 (Authentication) 규칙 커스터마이즈하기
기본적으로 Protected App은 모든 경로를 보호합니다. 인증 (Authentication) 규칙을 커스터마이즈해야 한다면, Console에서 "Custom authentication rules" 필드를 설정할 수 있습니다.
정규 표현식을 지원하며, 다음은 두 가지 예시입니다:
/admin및/privacy경로만 인증 (Authentication)으로 보호하려면:^/(admin|privacy)/.*- JPG 이미지는 인증 (Authentication)에서 제외하려면:
^(?!.*\.jpg$).*$
로컬 개발
Protected App은 원본 서버와 함께 동작하도록 설계되었습니다. 하지만 원본 서버가 공개적으로 접근할 수 없는 경우, ngrok 또는 Cloudflare Tunnels과 같은 도구를 사용하여 로컬 서버를 인터넷에 노출할 수 있습니다.
SDK 통합으로 전환하기
Protected App은 인증 (Authentication) 과정을 단순화하도록 설계되었습니다. 하지만 더 나은 제어와 커스터마이즈를 위해 SDK 통합으로 전환하려면, Logto에서 새 애플리케이션 생성 후 SDK 통합을 설정할 수 있습니다. 원활한 전환을 위해 Protected App의 애플리케이션 설정을 재사용할 수 있습니다. Protected App은 실제로 Logto에서 "Traditional Web App" 유형이며, 애플리케이션 설정에서 "AppId"와 "AppSecret"을 찾을 수 있습니다. 전환이 완료되면 Protected App을 애플리케이션에서 제거할 수 있습니다.
관련 리소스
Protected App: 클릭 몇 번으로 앱의 인증 (Authentication) 구축하기. 코드 불필요.
Protected App의 개발 동기인증 (Authentication) 시스템을 가장 빠르게 구축하는 방법