IDME PLUGIN - nself-org/nchat GitHub Wiki
Plugin Name: idme
Version: 1.0.0
Category: Authentication
Status: Production Ready
Priority: MEDIUM
The ID.me Plugin enables identity verification and specialized login for military members, veterans, first responders, students, teachers, and other affiliation groups. It integrates ID.me's OAuth 2.0 authentication and identity verification services.
- ✅ OAuth 2.0 Authentication - Secure login with ID.me
- ✅ Identity Verification - Real-time identity proofing
- ✅ Group Affiliation - Military, student, teacher verification
- ✅ Credential Verification - Secure document verification
- ✅ Profile Integration - Import user profile data
- ✅ Scoped Access - Request specific data permissions
- ✅ Military - Active duty, veterans, families
- ✅ First Responders - Police, fire, EMS, nurses
- ✅ Students - K-12, college, university
- ✅ Teachers - K-12, college, university
- ✅ Government - Federal, state, local employees
- ✅ Healthcare - Medical professionals
- ID.me developer account (https://developer.id.me)
- OAuth application credentials
- HTTPS enabled (required for OAuth)
-
Create Developer Account:
- Visit https://developer.id.me
- Sign up for developer access
- Create new application
-
Configure OAuth Application:
- Application Name:
ɳChat - Redirect URI:
https://yourdomain.com/api/auth/oauth/callback - Scopes:
openid,profile,email,military,student,teacher
- Application Name:
-
Get Credentials:
- Client ID:
your_client_id - Client Secret:
your_client_secret
- Client ID:
cd /Users/admin/Sites/nself-nchat/backend
nself plugin install idmeAdd to backend/.env.plugins:
# ID.me Plugin
IDME_ENABLED=true
IDME_CLIENT_ID=your_client_id_here
IDME_CLIENT_SECRET=your_client_secret_here
IDME_REDIRECT_URI=https://${BASE_DOMAIN}/api/auth/oauth/callback
IDME_SCOPE=openid,profile,email,military,student,teacher
IDME_ENVIRONMENT=sandbox # or 'production'
# Optional: Group-specific scopes
IDME_VERIFY_MILITARY=true
IDME_VERIFY_STUDENT=true
IDME_VERIFY_TEACHER=true
IDME_VERIFY_FIRST_RESPONDER=trueAdd to frontend .env.local:
NEXT_PUBLIC_IDME_ENABLED=truenself restartGET /api/auth/oauth/authorize?provider=idmeResponse:
{
"authUrl": "https://api.id.me/oauth/authorize?client_id=xxx&redirect_uri=xxx&scope=openid+profile+email&state=xxx"
}User is redirected to ID.me to:
- Log in with ID.me account
- Verify identity (if first time)
- Grant permissions to your app
GET /api/auth/oauth/callback?provider=idme&code=xxx&state=xxxBackend Process:
- Exchange code for access token
- Fetch user profile
- Verify group affiliations
- Create/update user account
- Create session
- Redirect to app
GET /api/auth/oauth/authorize?provider=idmeResponse:
{
"authUrl": "https://api.id.me/oauth/authorize?...",
"state": "random-state-string"
}GET /api/auth/oauth/callback?provider=idme&code=xxx&state=xxxSuccess Response:
- Redirects to
/with session cookie
Error Response:
- Redirects to
/login?error=oauth_failed
GET /api/auth/profile
Authorization: Bearer <access-token>Response:
{
"id": "idme-user-id",
"email": "[email protected]",
"firstName": "John",
"lastName": "Doe",
"verified": true,
"groups": [
{
"type": "military",
"status": "veteran",
"branch": "army",
"verified": true
},
{
"type": "student",
"institution": "MIT",
"verified": true
}
],
"verificationDate": "2026-02-03T12:00:00Z"
}POST /api/auth/oauth/disconnect
Authorization: Bearer <access-token>
Content-Type: application/json
{
"provider": "idme"
}import { useAuth } from '@/contexts/auth-context'
function LoginPage() {
const { loginWithOAuth } = useAuth()
const handleIDmeLogin = async () => {
try {
await loginWithOAuth('idme')
// User will be redirected to ID.me
} catch (error) {
console.error('ID.me login failed:', error)
}
}
return (
<button onClick={handleIDmeLogin} className="idme-button">
<img src="/logos/idme.svg" alt="ID.me" />
Sign in with ID.me
</button>
)
}import { useUser } from '@/hooks/use-user'
function ProfileBadges() {
const { user } = useUser()
const militaryVerified = user?.groups?.some(
(g) => g.type === 'military' && g.verified
)
const studentVerified = user?.groups?.some(
(g) => g.type === 'student' && g.verified
)
return (
<div>
{militaryVerified && (
<Badge variant="military">
<Shield /> Military Verified
</Badge>
)}
{studentVerified && (
<Badge variant="student">
<GraduationCap /> Student Verified
</Badge>
)}
</div>
)
}import { IDmeProvider } from '@/services/auth/providers/idme.provider'
const idmeProvider = new IDmeProvider()
// Request specific verification
const authUrl = await idmeProvider.getAuthorizationUrl({
scopes: ['military', 'student'],
affiliationRequired: true,
})
// Redirect user
window.location.href = authUrl| Scope | Description | Required |
|---|---|---|
openid |
OpenID Connect authentication | Yes |
profile |
Basic profile information | Yes |
email |
Email address | Yes |
| Scope | Description | Group Type |
|---|---|---|
military |
Military verification | Military |
student |
Student verification | Student |
teacher |
Teacher verification | Teacher |
first_responder |
First responder verification | First Responder |
government |
Government employee | Government |
healthcare |
Healthcare professional | Healthcare |
ID.me provides different levels of identity verification:
- Level 1 (IAL1): Basic email verification
- Level 2 (IAL2): Government ID verification
- Level 3 (IAL3): In-person identity proofing
Each affiliation group requires specific documentation:
- Military: DD-214, military ID, VA card
- Student: Student ID, enrollment letter
- Teacher: School email, teaching license
- First Responder: Department ID, certification
// Require military verification for channel access
const militaryChannel = {
name: 'Military Only',
access: {
requireVerification: true,
allowedGroups: ['military'],
},
}
// Check access in middleware
if (!user.groups?.some((g) => g.type === 'military' && g.verified)) {
return res.status(403).json({ error: 'Military verification required' })
}// Grant student role for discounts
if (user.groups?.some((g) => g.type === 'student' && g.verified)) {
user.roles.push('student')
user.discountEligible = true
}// Teacher-only content
if (user.groups?.some((g) => g.type === 'teacher' && g.verified)) {
user.roles.push('educator')
user.accessLevel = 'premium'
}ID.me provides test accounts in sandbox mode:
# Sandbox credentials
Email: [email protected]
Password: (provided by ID.me)
Group: Military (verified)
Email: [email protected]
Password: (provided by ID.me)
Group: Student (verified)describe('ID.me OAuth', () => {
it('should redirect to ID.me authorization', async () => {
const response = await fetch('/api/auth/oauth/authorize?provider=idme')
const data = await response.json()
expect(data.authUrl).toContain('id.me')
expect(data.authUrl).toContain('client_id')
})
it('should handle callback with code', async () => {
const response = await fetch(
'/api/auth/oauth/callback?provider=idme&code=test-code&state=test-state'
)
expect([200, 302]).toContain(response.status)
})
})Problem: "redirect_uri mismatch" error
Solutions:
- Verify redirect URI in ID.me dashboard matches exactly
- Check protocol (must be HTTPS in production)
- Ensure no trailing slashes
Problem: User not verified for requested scope
Solutions:
- Check if user completed verification
- Request correct scope
- Guide user through verification process
Problem: "invalid_client" error
Solutions:
- Verify Client ID and Secret are correct
- Check environment (sandbox vs production)
- Ensure credentials are in correct
.envfile
- HTTPS Only: Always use HTTPS in production
- State Parameter: Validate state to prevent CSRF
- Token Storage: Store tokens securely (encrypted)
- Scope Limitation: Request minimum required scopes
- Session Expiry: Implement proper session management
// Store tokens encrypted
import { encrypt, decrypt } from '@/lib/crypto'
const encryptedToken = encrypt(accessToken, process.env.ENCRYPTION_KEY)
await db.user.update({
where: { id: user.id },
data: { idmeToken: encryptedToken },
})
// Decrypt when needed
const accessToken = decrypt(user.idmeToken, process.env.ENCRYPTION_KEY)import { captureEvent } from '@/lib/analytics'
// Track verification success
captureEvent('idme_verification_success', {
userId: user.id,
groupType: 'military',
verificationLevel: 'IAL2',
})
// Track verification failure
captureEvent('idme_verification_failed', {
userId: user.id,
error: error.message,
})await auditLog.create({
event: 'idme_verification',
userId: user.id,
metadata: {
groups: user.groups,
verifiedAt: new Date(),
},
})- Initial release
- OAuth 2.0 authentication
- Military, student, teacher verification
- Profile integration
- Group-based access control
- ID.me Developer Portal: https://developer.id.me
- ID.me Support: [email protected]
- Documentation: https://developer.id.me/documentation
- nself Discord: https://discord.gg/nself