Passport.js 애플리케이션에 인증 (Authentication)을 추가하세요
이 가이드는 Passport.js와 OIDC 전략을 사용하여 Logto를 애플리케이션에 통합하는 방법을 보여줍니다.
- 이 가이드에서는 프로젝트에 Express와 세션을 설정했다고 가정합니다. 설정하지 않았다면, Express.js 웹사이트를 방문하여 시작하세요.
사전 준비 사항
- Logto Cloud 계정 또는 셀프 호스팅 Logto.
- Logto 전통적인 애플리케이션 생성.
- 세션이 구성된 express 프로젝트. Express.js 웹사이트를 확인하세요.
설치
선호하는 패키지 관리자를 통해 Logto SDK를 설치하세요:
- npm
- pnpm
- yarn
npm i passport passport-openidconnectpnpm add passport passport-openidconnectyarn add passport passport-openidconnect통합
Passport.js 를 OIDC 전략으로 초기화하기
import passport from 'passport';
import OpenIDConnectStrategy, { type Profile, type VerifyCallback } from 'passport-openidconnect';
const endpoint = '<your-logto-endpoint>';
const appId = '<your-application-id>';
const appSecret = '<your-application-secret>';
export default function initPassport() {
passport.use(
new OpenIDConnectStrategy(
{
issuer: `${endpoint}/oidc`,
authorizationURL: `${endpoint}/oidc/auth`,
tokenURL: `${endpoint}/oidc/token`,
userInfoURL: `${endpoint}/oidc/me`,
clientID: appId,
clientSecret: appSecret,
callbackURL: '/callback',
scope: ['profile', 'offline_access'],
},
(issuer: string, profile: Profile, callback: VerifyCallback) => {
callback(null, profile);
}
)
);
passport.serializeUser((user, callback) => {
callback(null, user);
});
passport.deserializeUser(function (user, callback) {
callback(null, user as Express.User);
});
}
이 코드는 **OpenIDConnectStrategy**로 Passport를 초기화합니다. serialize 및 deserialize 메서드는 데모 목적으로 설정되어 있습니다.
애플리케이션에서 Passport 미들웨어를 초기화하고 첨부하세요:
import initPassport from './passport';
// ... other code
initPassport();
// ... other code
app.use(passport.authenticate('session'));
// ... other code
리디렉트 URI 구성하기
자세한 내용을 살펴보기 전에, 최종 사용자 경험에 대한 간단한 개요를 소개합니다. 로그인 과정은 다음과 같이 단순화할 수 있습니다:
- 귀하의 앱이 로그인 메서드를 호출합니다.
- 사용자는 Logto 로그인 페이지로 리디렉션됩니다. 네이티브 앱의 경우, 시스템 브라우저가 열립니다.
- 사용자가 로그인하면, 다시 귀하의 앱(리디렉션 URI로 설정됨)으로 리디렉션됩니다.
리디렉션 기반 로그인에 관하여
- 이 인증 과정은 OpenID Connect (OIDC) 프로토콜을 따르며, Logto는 사용자 로그인을 보호하기 위해 엄격한 보안 조치를 시행합니다.
- 여러 앱이 있는 경우, 동일한 아이덴티티 제공자 (Logto)를 사용할 수 있습니다. 사용자가 한 앱에 로그인하면, Logto는 사용자가 다른 앱에 접근할 때 자동으로 로그인 과정을 완료합니다.
리디렉션 기반 로그인에 대한 이론적 배경과 이점에 대해 더 알고 싶다면, Logto 로그인 경험 설명을 참조하세요.
다음 코드 스니펫에서는, 여러분의 앱이 http://localhost:3000/ 에서 실행되고 있다고 가정합니다.
리디렉션 URI 구성
Logto Console의 애플리케이션 세부 정보 페이지로 이동합니다. 리디렉션 URI http://localhost:3000/callback를 추가하세요.
로그인과 마찬가지로, 사용자는 공유 세션에서 로그아웃하기 위해 Logto로 리디렉션되어야 합니다. 완료되면 사용자를 다시 웹사이트로 리디렉션하면 좋습니다. 예를 들어, 로그아웃 후 리디렉션 URI 섹션에 http://localhost:3000/를 추가하세요.
그런 다음 "저장"을 클릭하여 변경 사항을 저장하세요.
로그인 및 로그아웃 구현하기
이제 인증 프로세스를 위한 특정 경로를 생성하겠습니다:
app.get('/sign-in', passport.authenticate('openidconnect'));
app.get(
'/callback',
passport.authenticate('openidconnect', {
successReturnToOrRedirect: '/',
})
);
app.get('/sign-out', (request, response, next) => {
request.logout((error) => {
if (error) {
next(error);
return;
}
response.redirect(`${endpoint}/oidc/session/end?client_id=${appId}`);
});
});
그런 다음 홈페이지에 추가하세요
app.get('/', (request: Request, response) => {
const { user } = request;
response.setHeader('content-type', 'text/html');
if (user) {
response.end(
`<h1>Hello Logto</h1><p>Signed in as ${JSON.stringify(
user
)}, <a href="/sign-out">Sign Out</a></p>`
);
} else {
response.end(`<h1>Hello Logto</h1><p><a href="/sign-in">Sign In</a></p>`);
}
});
체크포인트: 애플리케이션 테스트하기
이제 애플리케이션을 테스트할 수 있습니다:
- 애플리케이션을 실행하면 로그인 버튼이 표시됩니다.
- 로그인 버튼을 클릭하면 SDK가 로그인 프로세스를 초기화하고 Logto 로그인 페이지로 리디렉션됩니다.
- 로그인 후, 애플리케이션으로 다시 리디렉션되어 로그아웃 버튼이 표시됩니다.
- 로그아웃 버튼을 클릭하여 토큰 저장소를 지우고 로그아웃합니다.
스코프와 클레임
Logto는 OIDC 스코프 (Scope) 및 클레임 (Claim) 규칙을 사용하여 ID 토큰 및 OIDC userinfo 엔드포인트에서 사용자 정보를 가져오기 위한 스코프 (Scope)와 클레임 (Claim)을 정의합니다. "스코프 (Scope)"와 "클레임 (Claim)" 모두 OAuth 2.0 및 OpenID Connect (OIDC) 명세에서 온 용어입니다.
표준 OIDC 클레임 (Claim)의 경우, ID 토큰에 포함되는지는 요청된 스코프 (Scope)에 의해 엄격하게 결정됩니다. 확장 클레임 (예: custom_data 및 organizations)은 커스텀 ID 토큰 설정을 통해 ID 토큰에 추가로 포함되도록 구성할 수 있습니다.
요약하면, 스코프 (Scope)를 요청하면 해당하는 클레임 (Claim)을 사용자 정보에서 받을 수 있습니다. 예를 들어, `email` 스코프 (Scope)를 요청하면 사용자의 `email` 및 `email_verified` 데이터를 받게 됩니다.
기본적으로 Logto SDK는 항상 세 가지 스코프 (Scope)를 요청합니다: `openid`, `profile`, 그리고 `offline_access`입니다. 이 기본 스코프 (Scope)는 제거할 수 없지만, Logto를 구성할 때 더 많은 스코프 (Scope)를 추가할 수 있습니다:
export default function initPassport() {
passport.use(
new OpenIDConnectStrategy(
{
// ... other options
clientID: appId,
clientSecret: appSecret,
callbackURL: '/callback',
scope: ['openid', 'offline_access', 'profile', 'email'],
}
// ... other options
)
);
// ... other options
}
지원되는 스코프와 해당 클레임(Claim)의 목록은 다음과 같습니다:
표준 OIDC 스코프
openid (기본값)
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
| sub | string | 사용자의 고유 식별자 |
profile (기본값)
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
| name | string | 사용자의 전체 이름 |
| username | string | 사용자의 사용자명 |
| picture | string | 최종 사용자의 프로필 사진 URL. 이 URL은 이미지 파일(예: PNG, JPEG, GIF 이미지 파일)을 가리켜야 하며, 이미지를 포함한 웹 페이지가 아니어야 합니다. 이 URL은 최종 사용자를 설명할 때 표시하기에 적합한 프로필 사진을 명확히 참조해야 하며, 최종 사용자가 임의로 촬영한 사진이 아니어야 합니다. |
| created_at | number | 최종 사용자가 생성된 시간. 시간은 Unix epoch (1970-01-01T00:00:00Z) 이후 밀리초로 표시됩니다. |
| updated_at | number | 최종 사용자의 정보가 마지막으로 업데이트된 시간. 시간은 Unix epoch (1970-01-01T00:00:00Z) 이후 밀리초로 표시됩니다. |
기타 표준 클레임(Claim)에는 family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo, locale 등이 있으며, 이들은 userinfo 엔드포인트를 요청하지 않아도 profile 스코프에 포함됩니다. 위의 클레임과의 차이점은, 이 클레임들은 값이 비어 있지 않을 때만 반환되며, 위의 클레임들은 값이 비어 있으면 null을 반환합니다.
표준 클레임(Claim)과 달리, created_at 및 updated_at 클레임은 초 단위가 아닌 밀리초 단위를 사용합니다.
email
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
string | 사용자의 이메일 주소 | |
| email_verified | boolean | 이메일 주소가 인증되었는지 여부 |
phone
| 클레임(Claim) 이름 | 타입 | 설명 |
|---|---|---|
| phone_number | string | 사용자의 전화번호 |
| phone_number_verified | boolean | 전화번호가 인증되었는지 여부 |
address
주소 클레임(Claim)의 세부 사항은 OpenID Connect Core 1.0 을 참조하세요.
**(기본값)**으로 표시된 스코프는 항상 Logto SDK에서 요청합니다. 표준 OIDC 스코프의 클레임(Claim)은 해당 스코프가 요청될 때 항상 ID 토큰 (ID token)에 포함되며, 비활성화할 수 없습니다.
확장 스코프
다음 스코프는 Logto에서 확장한 것으로, userinfo 엔드포인트를 통해 클레임(Claim)을 반환합니다. 이 클레임들은 Console > Custom JWT를 통해 ID 토큰 (ID token)에 직접 포함되도록 설정할 수도 있습니다. 자세한 내용은 커스텀 ID 토큰을 참고하세요.
custom_data
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| custom_data | object | 사용자의 커스텀 데이터 |
identities
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| identities | object | 사용자의 연결된 아이덴티티 | |
| sso_identities | array | 사용자의 연결된 SSO 아이덴티티 |
roles
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| roles | string[] | 사용자의 역할 (Role) | ✅ |
urn:logto:scope:organizations
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| organizations | string[] | 사용자가 속한 조직 (Organization) ID | ✅ |
| organization_data | object[] | 사용자가 속한 조직 (Organization) 데이터 |
이러한 조직 (Organization) 클레임(Claim)은 불투명 토큰 (Opaque token)을 사용할 때도 userinfo 엔드포인트를 통해 조회할 수 있습니다. 그러나 불투명 토큰 (Opaque token)은 조직 토큰 (Organization token)으로 사용되어 조직별 리소스에 접근할 수 없습니다. 자세한 내용은 불투명 토큰 (Opaque token)과 조직 (Organization)을 참고하세요.
urn:logto:scope:organization_roles
| 클레임(Claim) 이름 | 타입 | 설명 | 기본적으로 ID 토큰에 포함됨 |
|---|---|---|---|
| organization_roles | string[] | 사용자가 속한 조직 (Organization)의 역할 (Role), 형식: <organization_id>:<role_name> | ✅ |