| name | openevidence-enterprise-rbac |
| description | Configure OpenEvidence enterprise SSO, role-based access control, and organization management.
Use when implementing SSO integration, configuring role-based permissions,
or setting up organization-level controls for clinical AI applications.
Trigger with phrases like "openevidence SSO", "openevidence RBAC",
"openevidence enterprise", "openevidence roles", "openevidence permissions".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
OpenEvidence Enterprise RBAC
Overview
Configure enterprise-grade access control for OpenEvidence clinical AI integrations in healthcare organizations.
Prerequisites
- OpenEvidence Enterprise tier subscription
- Identity Provider (IdP) with SAML/OIDC support
- Understanding of role-based access patterns
- HIPAA audit logging infrastructure
Role Definitions
| Role | Permissions | Use Case |
|---|
| Physician | Full clinical query, DeepConsult | Active patient care |
| Nurse | Clinical query (no DeepConsult) | Nursing support |
| Pharmacist | Drug-focused queries | Medication management |
| Resident | Clinical query (supervised) | Training |
| Admin | Full access, user management | Platform administration |
| Auditor | Read-only audit logs | Compliance review |
| Integration | API access only | System integration |
Instructions
Step 1: Role and Permission Definitions
export enum ClinicalRole {
Physician = 'physician',
Nurse = 'nurse',
Pharmacist = 'pharmacist',
Resident = 'resident',
Admin = 'admin',
Auditor = 'auditor',
Integration = 'integration',
}
export interface ClinicalPermissions {
clinicalQuery: boolean;
deepConsult: boolean;
drugInfo: boolean;
guidelineAccess: boolean;
exportResults: boolean;
viewAuditLogs: boolean;
manageUsers: boolean;
manageSettings: boolean;
}
export const ROLE_PERMISSIONS: Record<ClinicalRole, ClinicalPermissions> = {
[ClinicalRole.Physician]: {
clinicalQuery: true,
deepConsult: true,
drugInfo: true,
guidelineAccess: ,
: ,
: ,
: ,
: ,
},
[.]: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
[.]: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
[.]: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
[.]: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
[.]: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
[.]: {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
},
};
(): {
[role][permission];
}
(): {
[role];
}
Step 2: SSO Integration (SAML)
import { Strategy as SamlStrategy } from 'passport-saml';
import passport from 'passport';
interface SAMLConfig {
entryPoint: string;
issuer: string;
cert: string;
callbackUrl: string;
identifierFormat?: string;
}
export function configureSAML(config: SAMLConfig): void {
passport.use(
new SamlStrategy(
{
entryPoint: config.entryPoint,
issuer: config.issuer,
cert: config.cert,
callbackUrl: config.callbackUrl,
identifierFormat: config.identifierFormat || 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
},
async (profile, done) => {
try {
const user = await findOrCreateUser({
email: profile.,
: profile.,
: profile.,
: profile. || [],
});
role = (profile.);
(, { ...user, role });
} (error) {
(error);
}
}
)
);
}
: <, > = {
: .,
: .,
: .,
: .,
: .,
: .,
: .,
: .,
: .,
};
(): {
(groups.( [g] === .)) {
.;
}
(groups.( [g] === .)) {
.;
}
(groups.( [g] === .)) {
.;
}
(groups.( [g] === .)) {
.;
}
(groups.( [g] === .)) {
.;
}
.;
}
Step 3: OAuth2/OIDC Integration
import { Strategy as OpenIDConnectStrategy } from 'passport-openidconnect';
import passport from 'passport';
interface OIDCConfig {
issuer: string;
authorizationURL: string;
tokenURL: string;
userInfoURL: string;
clientID: string;
clientSecret: string;
callbackURL: string;
scope: string[];
}
export function configureOIDC(config: OIDCConfig): void {
passport.use(
new OpenIDConnectStrategy(
{
issuer: config.issuer,
authorizationURL: config.authorizationURL,
tokenURL: config.tokenURL,
userInfoURL: config.userInfoURL,
clientID: config.clientID,
clientSecret: config.clientSecret,
callbackURL: config.,
: config.,
},
(issuer, profile, done) => {
{
user = ({
: profile.?.[]?.,
: profile.?.,
: profile.?.,
: profile.,
});
role = (profile.);
(, { ...user, role });
} (error) {
(error);
}
}
)
);
}
(): <> {
(claims[]) {
claims[] ;
}
(claims[]) {
(claims[]);
}
.;
}
Step 4: Permission Middleware
import { Request, Response, NextFunction } from 'express';
import { ClinicalRole, hasPermission, ClinicalPermissions } from '../rbac/roles';
import { auditLogger } from '../compliance/audit-trail';
interface AuthenticatedRequest extends Request {
user: {
id: string;
email: string;
role: ClinicalRole;
};
}
export function requirePermission(permission: keyof ClinicalPermissions) {
return async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
const user = req.user;
if (!user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (!hasPermission(user., permission)) {
auditLogger.({
: user.,
: user.,
: ,
: permission,
: req.,
: req.,
: req.() || ,
});
res.().({
: ,
: ,
: (permission),
});
}
();
};
}
(): [] {
.()
.( perms[permission])
.( role );
}
app.(,
(),
clinicalQueryHandler
);
app.(,
(),
deepConsultHandler
);
app.(,
(),
auditLogsHandler
);
Step 5: Organization Management
interface Organization {
id: string;
name: string;
openEvidenceOrgId: string;
ssoEnabled: boolean;
enforceSso: boolean;
allowedDomains: string[];
defaultRole: ClinicalRole;
settings: OrganizationSettings;
}
interface OrganizationSettings {
deepConsultEnabled: boolean;
maxDeepConsultsPerDay: number;
auditLogRetentionDays: number;
allowExport: boolean;
requireMFA: boolean;
}
export class OrganizationManager {
constructor(private db: Database) {}
async createOrganization(config: Omit<Organization, 'id'>): Promise<Organization> {
const org = await this.db.organizations.({
: {
...config,
: crypto.(),
},
});
org;
}
(
: ,
: <>
): <> {
...({
: { : orgId },
: { settings },
});
}
(: , : , : ): <> {
...({
: {
: orgId,
userId,
role,
: (),
},
});
}
(: , : , : ): <> {
...({
: { : { : orgId, userId } },
: { : newRole },
});
}
(: , : ): <> {
...({
: { : { : orgId, userId } },
});
}
(: ): < | > {
membership = ...({
: { userId },
: { : },
});
membership?. || ;
}
}
Step 6: Session Management
import session from 'express-session';
import RedisStore from 'connect-redis';
export function configureSession(redis: Redis): session.SessionOptions {
return {
store: new RedisStore({ client: redis }),
secret: process.env.SESSION_SECRET!,
name: 'clinical.session',
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 8 * 60 * 60 * 1000,
sameSite: 'strict',
},
rolling: true,
};
}
export function sessionTimeoutMiddleware(warningMinutes: = ) {
{
(req.?.?.) {
remainingMs = req...;
warningMs = warningMinutes * * ;
(remainingMs < warningMs) {
res.(, );
}
}
();
};
}
() {
(: , : , : ) => {
lastAuth = req.?.;
(!lastAuth) {
res.().({ : });
}
ageMs = .() - (lastAuth).();
maxAgeMs = maxAgeMinutes * * ;
(ageMs > maxAgeMs) {
res.().({
: ,
: ,
});
}
();
};
}
Output
- Role definitions with clinical permissions
- SAML/OIDC SSO integration
- Permission middleware
- Organization management
- Secure session handling
RBAC Checklist
Error Handling
| RBAC Issue | Detection | Resolution |
|---|
| SSO login fails | Auth callback error | Check IdP configuration |
| Wrong role assigned | User reports | Review group mappings |
| Permission denied | 403 responses | Check role permissions |
| Session expired | User redirect | Implement session warning |
Resources
Next Steps
For EHR integration migrations, see openevidence-migration-deep-dive.