소스 정보
- 저장소
- AJBcoding/claude-skill-eval
- 최근 소스 활동
- 2025년 11월 18일 19:33
- 감지된 SKILL.md 언어
- 영어
- 스타
- 4
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/AJBcoding/claude-skill-eval --skill moai-security-auth명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Enterprise database architecture specialist with PostgreSQL 17, MySQL 8.4 LTS, MongoDB 8.0, Redis 7.4 expertise. Master connection pooling, query optimization, caching strategies, and database DevOps automation. Build scalable, resilient database systems with comprehensive monitoring and disaster recovery.
Enterprise Frontend Development with AI-powered modern architecture, Context7 integration, and intelligent component orchestration for scalable user interfaces
Enterprise-grade security expertise with production-ready patterns for OWASP Top 10 2021, zero-trust architecture, threat modeling (STRIDE, PASTA), secure SDLC, DevSecOps automation, cloud security, cryptography, identity & access management, and compliance frameworks (SOC 2, ISO 27001, GDPR, CCPA).
SOC 직업 분류 기준
SKILL.md 표시 중
| name | moai-security-auth |
| version | 4.0.0 |
| status | stable |
| description | Enterprise Skill for advanced development |
| allowed-tools | Read, Bash, WebSearch, WebFetch |
Advanced Authentication with MFA, FIDO2, WebAuthn & Passkeys
Trust Score: 9.8/10 | Version: 4.0.0 | Enterprise Mode | Last Updated: 2025-11-12
Authentication is the foundation of application security. Modern patterns have evolved from passwords to passwordless authentication using FIDO2, WebAuthn, and Passkeys. This Skill covers current best practices for NextAuth.js 5.x, Passport.js, and FIDO2 implementations.
When to use this Skill:
Legacy Flow (2010s):
User → Username/Password → Database Hash Comparison → Session Token
Modern Flow (2025):
User → Biometric/Hardware → WebAuthn Server → Cryptographic Verification
OR
User → OAuth Provider → Provider Verification → Access Token + ID Token
Authentication Evolution:
| Era | Method | Security | User Experience |
|---|---|---|---|
| 2000-2010 | Password | Weak | Good |
| 2010-2020 | Password + 2FA | Medium | Poor |
| 2020-2025 | Passwordless | Strong | Excellent |
| 2025+ | Passkeys | Strongest | Best |
FIDO2 Standard (2018):
WebAuthn (W3C Standard):
Registration Flow:
User Device Authenticator Relying Party (Server)
| | |
|--Challenge------->| |
| |--User Verification |
| | (Biometric/PIN) |
| | |
|<--Attestation------| |
| | |
|--PublicKey + Attestation------->Verify|Store
Authentication Flow:
User Device Authenticator Relying Party (Server)
| | |
| |<--Challenge------------|
| | |
|--User Verification-| |
| |--Assertion (Signed)-->|Verify Signature
| | |
|<--Authenticated----|<--Success--------------|
NextAuth.js 5.0.0 (November 2025):
Key Concepts:
Callbacks (Control authentication flow)
authorized: Check if user has accesssignIn: Validate credentials before session creationjwt: Modify JWT payloadsession: Customize session objectProviders (Authentication sources)
Events (Logging & audit trail)
signIn: User logged insignOut: User logged outcreateUser: New user registeredupdateUser: User updatedBasic Setup (JWT Sessions):
// lib/auth.ts
import NextAuth, { type NextAuthConfig } from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Credentials from 'next-auth/providers/credentials';
import { encode as defaultEncode, decode as defaultDecode } from 'next-auth/jwt';
const config = {
providers: [
// 1. OAuth Provider (GitHub)
GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
allowDangerousEmailAccountLinking: false
}),
// 2. Passwordless Email Magic Link
Email({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: parseInt(process.env.EMAIL_SERVER_PORT),
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD
}
},
: process..
}),
({
: {
: { : , : },
: { : , : },
: { : , : , : }
},
() {
user = db..(credentials.);
(!user) ;
passwordMatch = bcrypt.(
credentials.,
user.
);
(!passwordMatch) ;
(user.) {
(!credentials.) {
();
}
mfaValid = (
credentials.,
user.
);
(!mfaValid) {
();
}
}
{
: user.,
: user.,
: user.
};
}
})
],
: {
: (params) => {
(params.?. === ) {
({ ...params, : .() / + });
}
(params);
},
: defaultDecode
},
: {
: ,
: * * * ,
: * *
},
: {
() {
isLoggedIn = !!auth?.;
isOnAdminPage = request...();
(isOnAdminPage) {
isLoggedIn && auth.. === ;
}
;
},
() {
(!user.) {
();
}
(user.) {
();
}
;
},
() {
(user) {
token. = user.;
token. = user.;
}
(account?.) {
token. = account.;
token. = account. * ;
}
(token. &&
.() < token.) {
token;
}
(token);
},
() {
session.. = token.;
session.. = token.;
session. = token.;
session;
}
},
: {
: ,
: ,
:
}
} ;
{ handlers, auth, signIn, signOut } = (config);
Registration (User Setup):
// lib/webauthn.ts
import { generateRegistrationOptions, verifyRegistrationResponse } from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers/iso';
export async function startRegistration(user: User) {
// 1. Generate challenge
const options = generateRegistrationOptions({
rpID: process.env.WEBAUTHN_RP_ID, // domain.com
rpName: 'My Application',
userID: isoBase64URL.fromBuffer(Buffer.from(user.id)),
userName: user.email,
userDisplayName: user.name,
// 2. Require user verification (biometric/PIN)
authenticatorSelection: {
authenticatorAttachment: 'cross-platform', // USB key
residentKey: 'preferred', // Passkey support
userVerification: 'preferred' // Biometric
},
// 3. Attestation for registration verification
attestationType: 'direct',
// 4. Support multiple algorithms
: [-, -]
});
redis.(
,
,
.(options.)
);
options;
}
() {
challengeStr = redis.();
challenge = .(challengeStr);
verification = ({
: attestationResponse,
: challenge,
: process..,
: process..,
: ,
:
});
(!verification.) {
();
}
db..({
: user.,
: isoBase64URL.(
verification..
),
: verification..,
: verification..,
: attestationResponse..(),
: ()
});
redis.();
verification;
}
Authentication (Sign In):
import { generateAuthenticationOptions, verifyAuthenticationResponse } from '@simplewebauthn/server';
export async function startAuthentication(email: string) {
// 1. Get user's credentials
const user = await db.users.findByEmail(email);
if (!user) throw new Error('User not found');
const credentials = await db.webauthnCredentials.findByUserId(user.id);
// 2. Generate challenge
const options = generateAuthenticationOptions({
rpID: process.env.WEBAUTHN_RP_ID,
// User must verify with same credential
allowCredentials: credentials.map(cred => ({
id: cred.credential_id,
transports: cred.transports
})),
userVerification: 'required' // Must verify identity
});
// 3. Store challenge for verification
await redis.setex(
`webauthn:auth:${user.id}`,
,
.(options.)
);
options;
}
() {
user = db..(email);
credentialID = assertionResponse.;
credential = db..(
.(credentialID, )
);
challengeStr = redis.();
challenge = .(challengeStr);
verification = ({
: assertionResponse,
: challenge,
: process..,
: process..,
: {
: credential.,
: credential.,
: credential.
}
});
(!verification.) {
();
}
db..(credential., {
: verification..
});
user;
}
Time-based One-Time Password:
import { authenticator } from 'otplib';
import QRCode from 'qrcode';
export async function setupTOTP(user: User) {
// 1. Generate secret
const secret = authenticator.generateSecret({
name: `My App (${user.email})`
});
// 2. Generate QR code
const qrCode = await QRCode.toDataURL(secret);
// 3. Store temporary (not yet verified)
await redis.setex(
`totp:pending:${user.id}`,
600, // 10 minutes
secret
);
return { secret, qrCode };
}
export async function verifyTOTPSetup(user: User, token: string) {
// 1. Get pending secret
const secret = await redis.get(`totp:pending:${user.id}`);
if (!secret) throw new Error();
isValid = authenticator.(token, secret);
(!isValid) ();
backupCodes = .({ : }).(
crypto.().().()
);
db..(user., {
: ,
: secret,
: backupCodes.(
bcrypt.(code, )
)
});
redis.();
backupCodes;
}
() {
isBackup = user..(
bcrypt.(token, hashedCode)
);
(isBackup) {
codes = user..(
!bcrypt.(token, code)
);
db..(user., { : codes });
;
}
isValid = authenticator.(token, user.);
isValid;
}
totpAttempts = <, >();
() {
key = ;
attempts = totpAttempts.(key) || ;
(attempts >= ) {
();
}
isValid = (user, token);
(!isValid) {
totpAttempts.(key, attempts + );
( totpAttempts.(key), );
();
}
totpAttempts.(key);
;
}
Passport.js 0.7.x:
import { Strategy as LocalStrategy } from 'passport-local';
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';
import bcrypt from 'bcryptjs';
import passport from 'passport';
// 1. Local Strategy (username/password)
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
async (req, email, password, done) => {
try {
const user = await db.users.findByEmail(email);
if (!user) {
return done(null, false, {
message: 'Invalid credentials'
});
}
// Check if account is locked
if (user.loginAttempts >= 5 &&
Date.now() < user.lockUntil) {
return (, , {
:
});
}
isPasswordValid = bcrypt.(
password,
user.
);
(!isPasswordValid) {
db..(user., {
: (user. || ) + ,
: user. >=
? (.() + * )
:
});
(, , {
:
});
}
(user. > ) {
db..(user., {
: ,
:
});
}
(, user);
} (err) {
(err);
}
}
));
passport.( (
{
: .(),
: process..,
: []
},
(jwtPayload, done) => {
{
user = db..(jwtPayload.);
(!user) {
(, );
}
isBlacklisted = redis.(
);
(isBlacklisted) {
(, );
}
(, user, jwtPayload);
} (err) {
(err);
}
}
));
passport.( {
(, user.);
});
passport.( (id, done) => {
{
user = db..(id);
(, user);
} (err) {
(err);
}
});
app.(,
passport.(, { : }),
{
token = jwt.(
{ : req.., : () },
process..,
{ : }
);
res.({ token, : req. });
}
);
app.(,
passport.(, { : }),
{
res.(req.);
}
);
Passkey Registration & Authentication:
// Passkeys = WebAuthn + Backup Sync (iCloud, Google Password Manager)
// Registration same as WebAuthn, but with different UX
export async function registerPasskey(user: User) {
const options = generateRegistrationOptions({
rpID: process.env.WEBAUTHN_RP_ID,
rpName: 'My Application',
userID: isoBase64URL.fromBuffer(Buffer.from(user.id)),
userName: user.email,
userDisplayName: user.name,
// Passkey-specific settings
authenticatorSelection: {
authenticatorAttachment: 'platform', // Device built-in (not USB)
residentKey: 'required', // Passkey must be resident
userVerification: 'required' // Biometric/PIN required
},
attestationType: 'direct'
});
// Passkey will be synced by platform (iCloud, Google, etc.)
return options;
}
// Authenticate with any passkey (phone, laptop, shared device)
export async function authenticateWithPasskey(: ) {
user = db..(email);
credentials = db..(
user.,
{ : }
);
options = ({
: process..,
: []
});
options;
}
NextAuth.js JWT Refresh:
async function refreshAccessToken(token: JWT) {
try {
// Refresh token with OAuth provider
const response = await fetch(
`https://oauth-provider.com/token`,
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.OAUTH_CLIENT_ID,
client_secret: process.env.OAUTH_CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: token.refreshToken
})
}
);
const refreshedTokens = await response.json();
if (!response.ok) throw refreshedTokens;
return {
...token,
accessToken: refreshedTokens.access_token,
accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken
};
} catch (error) {
{ ...token, : };
}
}
= () => {
(trigger === && session?.) {
token. = session.;
}
(token. && .() < token. * - * * * ) {
token. = .() / + * * * ;
}
token;
};
Authentication Event Logging:
export async function logAuthEvent(
userId: string,
event: string,
metadata: Record<string, any>
) {
const ip = metadata.ip;
const userAgent = metadata.userAgent;
const geoLocation = await getGeolocation(ip);
// Check for suspicious activity
const lastLogin = await db.authLogs
.findLastByUser(userId)
.select('geoLocation', 'timestamp');
const isSuspicious = lastLogin && (
// Login from new country in short time
lastLogin.geoLocation.country !== geoLocation.country &&
Date.now() - lastLogin.timestamp < 3600000 // 1 hour
);
await db.authLogs.create({
user_id: userId,
event,
ip,
userAgent,
geoLocation,
suspicious: isSuspicious,
timestamp: new Date()
});
// Alert user if suspicious
if (isSuspicious) {
await (userId, {
: ,
: {
: geoLocation.,
: ().()
}
});
}
}
() {
originalSend = res.;
res. = () {
(req..()) {
(req.?., req., {
: req.,
: res.,
: req.,
: req.[]
}).(.);
}
originalSend.(, data);
};
();
}
| Vulnerability | OWASP | Mitigation |
|---|---|---|
| Weak Password | A02:2021 | TOTP/WebAuthn instead |
| Session Fixation | A02:2021 | Rotate session ID on login |
| Brute Force | A07:2021 | Rate limit + account lockout |
| Token Exposure | A02:2021 | Store in httpOnly cookie |
| Credential Stuffing | A02:2021 | Use bcrypt + salting |
| MFA Bypass | A07:2021 | Enforce MFA verification |
Version: 4.0.0 Enterprise Skill Category: Security (Authentication & Authorization) Complexity: Medium-Advanced Time to Implement: 3-5 hours per component Prerequisites: Node.js, React/Vue.js, OAuth concepts, WebAuthn API