05262157
This commit is contained in:
parent
251260aace
commit
0e08e6ff2c
|
@ -0,0 +1,21 @@
|
|||
TIMEZONE=Asia/Singapore
|
||||
|
||||
# PostgreSQL 配置
|
||||
POSTGRES_VERSION=latest
|
||||
|
||||
# PostgreSQL Env
|
||||
POSTGRES_DB=nice
|
||||
POSTGRES_USER=nice
|
||||
POSTGRES_PASSWORD=nice
|
||||
|
||||
# Redis 配置
|
||||
REDIS_VERSION=7.2.4
|
||||
REDIS_PASSWORD=nice
|
||||
|
||||
# MinIO 配置
|
||||
MINIO_VERSION=latest
|
||||
MINIO_ACCESS_KEY=nice
|
||||
MINIO_SECRET_KEY=nice123
|
||||
# Elasticsearch 配置
|
||||
ELASTIC_VERSION=9.0.1
|
||||
ELASTIC_PASSWORD=nice_elastic_password
|
|
@ -44,6 +44,7 @@ app.get('/', (c) => {
|
|||
app.use('/oidc/*', async (c, next) => {
|
||||
// @ts-ignore
|
||||
await oidc.callback(c.req.raw, c.res.raw)
|
||||
return c.finalize()
|
||||
// return void 也可以
|
||||
return
|
||||
})
|
||||
export default app
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { Configuration } from 'oidc-provider';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { RedisAdapter } from './redis-adapter';
|
||||
|
||||
const config: Configuration = {
|
||||
adapter: RedisAdapter,
|
||||
clients: [
|
||||
{
|
||||
client_id: 'example-client',
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import type { Adapter, AdapterPayload } from 'oidc-provider';
|
||||
import redis from '../redis';
|
||||
|
||||
export class RedisAdapter implements Adapter {
|
||||
name: string;
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
key(id: string) {
|
||||
return `${this.name}:${id}`;
|
||||
}
|
||||
|
||||
async upsert(id: string, payload: AdapterPayload, expiresIn: number) {
|
||||
const key = this.key(id);
|
||||
await redis.set(key, JSON.stringify(payload), 'EX', expiresIn);
|
||||
if (payload && payload.grantId) {
|
||||
// 记录grantId到id的映射,便于revokeByGrantId
|
||||
await redis.sadd(this.grantKey(payload.grantId), id);
|
||||
await redis.expire(this.grantKey(payload.grantId), expiresIn);
|
||||
}
|
||||
if (payload && payload.userCode) {
|
||||
await redis.set(this.userCodeKey(payload.userCode), id, 'EX', expiresIn);
|
||||
}
|
||||
if (payload && payload.uid) {
|
||||
await redis.set(this.uidKey(payload.uid), id, 'EX', expiresIn);
|
||||
}
|
||||
}
|
||||
|
||||
async find(id: string) {
|
||||
const data = await redis.get(this.key(id));
|
||||
return data ? JSON.parse(data) : undefined;
|
||||
}
|
||||
|
||||
async findByUserCode(userCode: string) {
|
||||
const id = await redis.get(this.userCodeKey(userCode));
|
||||
return id ? this.find(id) : undefined;
|
||||
}
|
||||
|
||||
async findByUid(uid: string) {
|
||||
const id = await redis.get(this.uidKey(uid));
|
||||
return id ? this.find(id) : undefined;
|
||||
}
|
||||
|
||||
async destroy(id: string) {
|
||||
const data = await this.find(id);
|
||||
await redis.del(this.key(id));
|
||||
if (data && data.grantId) {
|
||||
await redis.srem(this.grantKey(data.grantId), id);
|
||||
}
|
||||
if (data && data.userCode) {
|
||||
await redis.del(this.userCodeKey(data.userCode));
|
||||
}
|
||||
if (data && data.uid) {
|
||||
await redis.del(this.uidKey(data.uid));
|
||||
}
|
||||
}
|
||||
|
||||
async revokeByGrantId(grantId: string) {
|
||||
const key = this.grantKey(grantId);
|
||||
const ids = await redis.smembers(key);
|
||||
if (ids && ids.length) {
|
||||
await Promise.all(ids.map((id) => this.destroy(id)));
|
||||
}
|
||||
await redis.del(key);
|
||||
}
|
||||
|
||||
async consume(id: string) {
|
||||
const key = this.key(id);
|
||||
const data = await this.find(id);
|
||||
if (data) {
|
||||
data.consumed = Math.floor(Date.now() / 1000);
|
||||
await redis.set(key, JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
grantKey(grantId: string) {
|
||||
return `${this.name}:grant:${grantId}`;
|
||||
}
|
||||
userCodeKey(userCode: string) {
|
||||
return `${this.name}:userCode:${userCode}`;
|
||||
}
|
||||
uidKey(uid: string) {
|
||||
return `${this.name}:uid:${uid}`;
|
||||
}
|
||||
}
|
|
@ -1,2 +1 @@
|
|||
|
||||
export * from "./useEntity"
|
||||
|
|
|
@ -98,121 +98,3 @@ model UserLastVisit {
|
|||
@@index([userId, resourceType])
|
||||
@@map("user_last_visit")
|
||||
}
|
||||
|
||||
// OIDC 客户端相关模型
|
||||
model OidcClient {
|
||||
id String @id @default(cuid())
|
||||
clientId String @unique @map("client_id")
|
||||
clientSecret String? @map("client_secret")
|
||||
clientName String @map("client_name")
|
||||
clientUri String? @map("client_uri")
|
||||
logoUri String? @map("logo_uri")
|
||||
contacts String[]
|
||||
redirectUris String[] @map("redirect_uris")
|
||||
postLogoutRedirectUris String[] @map("post_logout_redirect_uris")
|
||||
tokenEndpointAuthMethod String @map("token_endpoint_auth_method")
|
||||
grantTypes String[] @map("grant_types")
|
||||
responseTypes String[] @map("response_types")
|
||||
scope String
|
||||
jwksUri String? @map("jwks_uri")
|
||||
jwks String?
|
||||
policyUri String? @map("policy_uri")
|
||||
tosUri String? @map("tos_uri")
|
||||
requirePkce Boolean @default(false) @map("require_pkce")
|
||||
active Boolean @default(true)
|
||||
createdBy String? @map("created_by")
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
lastModifiedTime DateTime? @updatedAt @map("last_modified_time")
|
||||
|
||||
// 关联模型
|
||||
consents OidcConsent[]
|
||||
authorizationCodes OidcCode[]
|
||||
tokens OidcToken[]
|
||||
|
||||
@@map("oidc_clients")
|
||||
}
|
||||
|
||||
// 用户同意记录
|
||||
model OidcConsent {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
clientId String @map("client_id")
|
||||
scope String
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
|
||||
// 关联
|
||||
client OidcClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, clientId])
|
||||
@@map("oidc_consents")
|
||||
}
|
||||
|
||||
// 授权码
|
||||
model OidcCode {
|
||||
id String @id @default(cuid())
|
||||
code String @unique
|
||||
userId String @map("user_id")
|
||||
clientId String @map("client_id")
|
||||
scope String
|
||||
redirectUri String @map("redirect_uri")
|
||||
codeChallenge String? @map("code_challenge")
|
||||
codeChallengeMethod String? @map("code_challenge_method")
|
||||
nonce String?
|
||||
authTime DateTime @default(now()) @map("auth_time")
|
||||
expiresAt DateTime @map("expires_at")
|
||||
used Boolean @default(false)
|
||||
|
||||
// 关联
|
||||
client OidcClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("oidc_authorization_codes")
|
||||
}
|
||||
|
||||
// 统一令牌表(合并access和refresh token)
|
||||
model OidcToken {
|
||||
id String @id @default(cuid())
|
||||
token String @unique
|
||||
userId String @map("user_id")
|
||||
clientId String @map("client_id")
|
||||
tokenType String @map("token_type") // "access" 或 "refresh"
|
||||
scope String
|
||||
expiresAt DateTime @map("expires_at")
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
isRevoked Boolean @default(false) @map("is_revoked")
|
||||
parentId String? @map("parent_id") // 用于关联refresh token和对应的access token
|
||||
|
||||
// 关联
|
||||
client OidcClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId, tokenType, isRevoked])
|
||||
@@map("oidc_tokens")
|
||||
}
|
||||
|
||||
// Session管理
|
||||
model OidcSession {
|
||||
id String @id @default(cuid())
|
||||
sessionId String @unique @map("session_id")
|
||||
userId String @map("user_id")
|
||||
expiresAt DateTime @map("expires_at")
|
||||
lastActive DateTime @default(now()) @map("last_active")
|
||||
deviceInfo String? @map("device_info")
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
lastModifiedTime DateTime? @updatedAt @map("last_modified_time")
|
||||
|
||||
@@map("oidc_sessions")
|
||||
}
|
||||
|
||||
// 供应商的密钥对
|
||||
model OidcKeyPair {
|
||||
id String @id @default(cuid())
|
||||
kid String @unique
|
||||
privateKey String @map("private_key")
|
||||
publicKey String @map("public_key")
|
||||
algorithm String
|
||||
active Boolean @default(true)
|
||||
createdTime DateTime @default(now()) @map("created_time")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
|
||||
@@map("oidc_key_pairs")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue