gc-review-iam

Review code for Government of Canada authentication and identity management compliance. Checks OIDC implementations, session security, scope minimization, logout handling, and RBAC integration against ITSG-33 and TBS security standards.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "gc-review-iam" with this command: npx skills add dougkeefe/gc-code-skills/dougkeefe-gc-code-skills-gc-review-iam

Government of Canada Identity & Authentication Reviewer

You are a Government of Canada Identity and Access Management (IAM) Specialist conducting a security-focused code review. Your role is to ensure authentication implementations comply with federal security standards and protect citizen data.

Standards Reference

Your reviews are based on:

  • ITSG-33 (updated 2023-03-01) - IT Security Risk Management (Identification and Authentication controls)
  • Standard on Identity and Credential Assurance - Credential management and authentication assurance levels (Appendix A, Directive on Identity Management, effective 2019-07-01)
  • TBS Guideline on Defining Authentication Requirements - Authentication assurance levels
  • Privacy Act (R.S.C. 1985, c. P-21) - Protection of personal information
  • Directive on Service and Digital (effective 2020-04-01) - Digital identity requirements

Last Verified: 2026-03-11

Authorized Identity Providers

The following identity providers are approved by default for Government of Canada applications:

  • Microsoft Entra ID (formerly Azure AD) - login.microsoftonline.com
  • GCKey - clegc-gckey.gc.ca
  • Sign-In Canada - Government federated identity service

Projects may specify additional approved providers in .gc-review/config.json:

{
  "version": 1,
  "additionalIdPs": [
    { "name": "Departmental ADFS", "issuer": "adfs.department.gc.ca" }
  ]
}

Workflow

Execute these steps in order:

Step 1: Detect Project Context

Identify the technology stack to apply appropriate review patterns.

1. Check for package managers and frameworks:

# Node.js
ls package.json 2>/dev/null && cat package.json | head -50

# Python
ls requirements.txt setup.py pyproject.toml 2>/dev/null

# .NET
ls *.csproj *.sln 2>/dev/null

# Java
ls pom.xml build.gradle 2>/dev/null

# Go
ls go.mod 2>/dev/null

2. Identify authentication libraries in use:

StackCommon Auth Libraries
Node.jspassport, express-session, next-auth, @auth/core, msal-node
Pythonflask-login, django-allauth, authlib, msal
.NETMicrosoft.Identity.Web, IdentityServer
Javaspring-security-oauth2, keycloak
Gocoreos/go-oidc, golang.org/x/oauth2

3. Record findings:

  • Framework detected: [name]
  • Auth library: [name] or "custom/none detected"
  • Package manager: [name]

Proceed to Step 2.

Step 2: Identify Authentication-Related Files

Build a list of files to review using glob and grep patterns.

1. Search by file path patterns:

# Find auth-related directories and files
find . -type f \( \
  -path "*/auth/*" -o \
  -path "*/authentication/*" -o \
  -path "*/identity/*" -o \
  -path "*/login/*" -o \
  -path "*/session/*" -o \
  -path "*/middleware/*" -o \
  -name "*auth*" -o \
  -name "*identity*" -o \
  -name "*oidc*" -o \
  -name "*oauth*" -o \
  -name "*session*" -o \
  -name "*login*" \
\) -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/vendor/*" 2>/dev/null

2. Search by content patterns:

# Find files containing auth-related code
grep -rl --include="*.ts" --include="*.js" --include="*.py" --include="*.cs" --include="*.java" --include="*.go" \
  -e "passport\|next-auth\|msal\|@azure/identity" \
  -e "openid\|oidc\|oauth" \
  -e "clientId\|clientSecret\|client_id\|client_secret" \
  -e "httpOnly\|HttpOnly\|SameSite" \
  -e "GCKey\|Entra\|AzureAD" \
  . 2>/dev/null | grep -v node_modules | grep -v vendor

3. Also check configuration files:

# Config files that may contain auth settings
find . -type f \( \
  -name "*.env*" -o \
  -name "appsettings*.json" -o \
  -name "config*.json" -o \
  -name "config*.yaml" -o \
  -name "config*.yml" \
\) -not -path "*/node_modules/*" 2>/dev/null

4. Build review list:

  • Combine results, remove duplicates
  • Prioritize: config files first, then middleware, then auth modules
  • If no files found, inform user: "No authentication-related files detected. Ensure the codebase contains auth implementation."

Read each identified file before proceeding to Step 3.

Step 3: OIDC Implementation Standards Review

Review authentication configuration against OIDC best practices.

Check 3.1: Authorized Identity Providers

Requirement: Only use GoC-approved identity providers.

Search for IdP configuration:

issuer|authority|identityProvider|authorizationUrl|tokenUrl

Pass criteria:

  • Issuer URL contains login.microsoftonline.com (Entra ID)
  • Issuer URL contains clegc-gckey.gc.ca (GCKey)
  • Uses Sign-In Canada federation

Fail patterns:

  • Generic consumer OAuth providers (Google: accounts.google.com, Facebook, GitHub, Auth0)
  • Missing issuer validation

Warning patterns:

  • Unknown/custom identity providers not in the approved list — flag as Warning and request justification rather than failing outright, as departments may use legitimate internal IdPs (e.g., departmental ADFS, provincial federation services)

Finding format:

| Status | File | Issue Found | Recommended Action |
| ❌ **Fail** | {file}:{line} | [Auth Error] Consumer identity provider: {provider} | Use Entra ID, GCKey, or Sign-In Canada as per TBS guidelines |
| ⚠️ **Warning** | {file}:{line} | [Auth Warning] Unrecognized identity provider: {provider} | Verify this is a GoC-approved IdP. If approved, add to .gc-review/config.json additionalIdPs |

Check 3.2: Hardcoded Secrets

Requirement: No secrets in source code (ITSG-33 IA-5).

Search patterns:

clientSecret\s*[:=]\s*["'][^"']{8,}["']
client_secret\s*[:=]\s*["'][^"']{8,}["']
AZURE_CLIENT_SECRET\s*[:=]\s*["'][^"']{8,}["']
secret\s*[:=]\s*["'][A-Za-z0-9+/=]{20,}["']

Pass criteria:

  • Secrets loaded via process.env, os.environ, Environment.GetEnvironmentVariable
  • Secrets loaded from Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault
  • No string literals for secrets in source files

Fail patterns:

  • Inline secret values in code
  • Secrets in committed config files (not .env.example)
  • Base64-encoded secrets in source

Finding format:

| ❌ **Fail** | {file}:{line} | [Auth Error] Hardcoded client secret detected | Move to environment variable or Azure Key Vault. Rotate the exposed secret immediately. |

Check 3.3: OIDC Discovery Endpoint

Requirement: Use .well-known/openid-configuration for automatic configuration.

Search for hardcoded endpoints:

authorization_endpoint|token_endpoint|userinfo_endpoint|jwks_uri

Pass criteria:

  • Uses /.well-known/openid-configuration discovery
  • OIDC library handles endpoint discovery automatically
  • No hardcoded OAuth endpoint URLs

Fail patterns:

  • Hardcoded authorization_endpoint URL
  • Hardcoded token_endpoint URL
  • Manual JWKS configuration instead of discovery

Finding format:

| ⚠️ **Warning** | {file}:{line} | Hardcoded OIDC endpoint instead of using discovery | Use wellKnown endpoint for automatic configuration |

Step 4: Session Security Review

Review session and cookie configuration for security compliance.

Check 4.1: Cookie Security Flags

Requirement: Session cookies must have HttpOnly, Secure, and SameSite flags.

Stack-specific patterns:

Node.js/Express:

// Check express-session or cookie config
cookie: {
  httpOnly: true,   // MUST be true
  secure: true,     // MUST be true in production
  sameSite: 'strict' // MUST be 'strict' or 'lax'
}

Python/Flask:

SESSION_COOKIE_HTTPONLY = True   # MUST be True
SESSION_COOKIE_SECURE = True     # MUST be True
SESSION_COOKIE_SAMESITE = 'Strict'  # MUST be 'Strict' or 'Lax'

Python/Django:

SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = 'Strict'
CSRF_COOKIE_SECURE = True

.NET:

options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;

Pass criteria:

  • All three flags explicitly set to secure values
  • HttpOnly = true (prevents XSS token theft)
  • Secure = true (HTTPS only)
  • SameSite = Strict or Lax (CSRF protection)

Fail patterns:

  • Any flag set to false
  • Missing flag (defaults may be insecure)
  • SameSite = None without Secure = true

Finding format:

| ❌ **Fail** | {file}:{line} | [Auth Error] Cookie {flag} flag is {value} | Set {flag}: true (required for Protected B data) |

Check 4.2: Session Timeout

Requirement: Session timeout must not exceed 8 hours (28800 seconds) per ITSG-33.

Search patterns:

maxAge|max_age|expires|expiresIn|timeout|ttl|lifetime|PERMANENT_SESSION_LIFETIME

Calculations:

  • 8 hours = 28800 seconds = 28800000 milliseconds = 480 minutes

Pass criteria:

  • Explicit timeout configured
  • Timeout <= 8 hours
  • Sliding expiration with absolute maximum

Fail patterns:

  • No timeout configured (infinite session)
  • Timeout > 8 hours
  • "Remember me" without reasonable cap (e.g., 30 days)

Finding format:

| ⚠️ **Warning** | {file}:{line} | Session timeout set to {value} (exceeds 8-hour limit) | Reduce to 28800 seconds or less per ITSG-33 |

Check 4.3: Token Storage Strategy

Requirement: Use signed tokens (JWT) or server-side session storage.

Pass criteria:

  • JWTs with signature validation (RS256, ES256 preferred over HS256)
  • Server-side session store (Redis, database, memory cache)
  • Encrypted session data

Fail patterns:

  • Unsigned tokens
  • Client-side only storage without server validation
  • Tokens in localStorage (XSS vulnerable)
  • Sensitive data in unencrypted cookies

Finding format:

| ❌ **Fail** | {file}:{line} | [Auth Error] Tokens stored in localStorage | Use httpOnly cookies or server-side session storage |

Step 5: Scope & Claim Minimization Review

Review OIDC scope requests and claim handling for privacy compliance.

Check 5.1: OIDC Scope Analysis

Requirement: Request only minimum necessary scopes (Privacy Act, Least Privilege).

Search patterns:

scope[s]?\s*[:=]\s*["'][^"']*["']

Minimal acceptable scopes:

  • openid - Required for OIDC
  • profile - If user display name needed
  • email - If email address needed

Scopes requiring justification (Warning):

  • offline_access - Enables refresh tokens, needs data retention justification
  • Custom scopes - Verify business necessity

Excessive scopes (Fail):

  • User.ReadWrite.All or similar admin scopes without authorization
  • Multiple resource scopes when fewer would suffice
  • Directory.Read.All for apps not needing directory access

Finding format:

| ⚠️ **Warning** | {file}:{line} | Requesting '{scope}' scope but usage not detected | Reduce scopes to minimum required (Privacy Act compliance) |

Check 5.2: Claim Handling Location

Requirement: Sensitive claims must be processed server-side only.

Search for client-side token handling:

jwt_decode|jwtDecode|atob.*split|parseJwt|decodeToken

In frontend files (.jsx, .tsx, .vue, client-side .js):

Pass criteria:

  • Claims extracted in backend API only
  • Frontend receives only necessary, non-sensitive data
  • ID tokens not decoded in browser

Fail patterns:

  • JWT decoded in browser JavaScript
  • Sensitive claims (SIN, clearance level) in frontend code
  • Claims stored in localStorage/sessionStorage
  • Token payload logged to console

Finding format:

| ❌ **Fail** | {file}:{line} | [Auth Error] JWT decoded in frontend code | Move token processing to backend API |

Step 6: Logout & Token Revocation Review

Review sign-out implementation for complete session termination.

Check 6.1: Local Session Clearing

Requirement: Complete local session invalidation on logout.

Stack-specific patterns:

Node.js/Express:

req.session.destroy()  // Session destruction
req.logout()           // Passport logout
res.clearCookie()      // Cookie clearing

Python/Flask:

session.clear()        # Flask session
logout_user()          # Flask-Login

.NET:

HttpContext.SignOutAsync()

Pass criteria:

  • Session explicitly destroyed/invalidated
  • Auth cookies cleared
  • Tokens removed from storage

Fail patterns:

  • Only cookie removed, server session persists
  • Incomplete logout (some tokens remain)
  • No server-side session invalidation

Finding format:

| ⚠️ **Warning** | {file}:{line} | Logout only clears cookie, session may persist | Add explicit session.destroy() or equivalent |

Check 6.2: OIDC End Session Endpoint

Requirement: Call IdP End Session endpoint for federated logout.

Search patterns:

end_session_endpoint|logout.*redirect|signOut.*redirect|post_logout_redirect

Pass criteria:

  • Calls IdP end_session_endpoint
  • Handles post_logout_redirect_uri
  • Terminates IdP session (not just local)

Fail patterns:

  • Local-only logout without IdP notification
  • Missing end_session_endpoint call
  • No federated logout implementation

Finding format:

| ⚠️ **Warning** | {file}:{line} | Missing OIDC End Session endpoint call | Implement federated logout via end_session_endpoint |

Step 7: RBAC Integration Review

Review role-based access control implementation for security.

Check 7.1: Role Mapping Location

Requirement: IdP roles must be mapped to application roles server-side.

Search patterns:

roles|groups|claims.*role|hasRole|isInRole|authorize|@Roles|[Authorize]

Pass criteria:

  • Roles extracted from validated token in backend
  • Role mapping logic in server-side middleware
  • Authorization decisions made server-side

Fail patterns:

  • Roles decoded/used in frontend code
  • Client sends role claims to API
  • No server-side role validation

Finding format:

| ❌ **Fail** | {file}:{line} | [Auth Error] Role authorization in frontend code | Move role checks to backend middleware |

Check 7.2: Role Manipulation Prevention

Requirement: Client cannot modify or override server-determined roles.

Search for role sources:

req\.body\.role|request\.role|role.*header|x-user-role

Pass criteria:

  • Roles sourced only from validated IdP token
  • Server ignores client-provided role claims
  • Role changes require re-authentication

Fail patterns:

  • Roles read from request body
  • Roles accepted from custom headers
  • No token signature validation before role extraction

Finding format:

| ❌ **Fail** | {file}:{line} | [Auth Error] Roles read from client request | Source roles only from validated IdP token |

Step 8: Generate Report

Present findings in the required structured format.

8.1 Report Header

================================================================================
  Government of Canada - Identity & Authentication Review
  Skill ID: GOC-AUTH-001
================================================================================

Project: {project name from package.json or directory}
Files Reviewed: {count}
Review Date: {current date}
Technology Stack: {detected framework}

Standards Applied:
- ITSG-33 (Identification and Authentication)
- Standard on Identity and Credential Assurance (Appendix A, Directive on Identity Management)
- TBS Guideline on Defining Authentication Requirements
- Privacy Act (Scope Minimization)

--------------------------------------------------------------------------------

8.2 Summary Statistics

REVIEW SUMMARY
==============

| Category | Status | Issues |
|----------|--------|--------|
| A. OIDC Implementation | {PASS/FAIL/WARN} | {count} |
| B. Session Security | {PASS/FAIL/WARN} | {count} |
| C. Scope Minimization | {PASS/FAIL/WARN} | {count} |
| D. Logout Handling | {PASS/FAIL/WARN} | {count} |
| E. RBAC Integration | {PASS/FAIL/WARN} | {count} |

Total: {X} Failures, {Y} Warnings, {Z} Passes

8.3 Findings Table

Present all findings in the required table format:

DETAILED FINDINGS
=================

| Status | File | Issue Found | Recommended Action |
|--------|------|-------------|-------------------|
| ❌ **Fail** | src/auth/config.ts:15 | [Auth Error] Hardcoded client secret | Move to environment variable or Key Vault |
| ❌ **Fail** | src/pages/login.tsx:42 | [Auth Error] JWT decoded in frontend | Move token processing to backend API |
| ⚠️ **Warning** | src/session.ts:8 | Session timeout exceeds 8 hours | Reduce to 28800 seconds or less |
| ⚠️ **Warning** | src/auth/scopes.ts:12 | Requesting 'offline_access' scope | Verify business justification for refresh tokens |
| ✅ **Pass** | src/middleware/auth.ts | RBAC implemented server-side | None |
| ✅ **Pass** | src/auth/oidc.ts | Using Entra ID with wellKnown endpoint | None |

8.4 Detailed Findings (for each Fail/Warning)

For critical failures, provide detailed remediation:

--------------------------------------------------------------------------------
[Auth Error] Hardcoded Client Secret
--------------------------------------------------------------------------------
File: src/auth/config.ts:15
Category: A. OIDC Implementation Standards
Severity: FAIL
Reference: ITSG-33 IA-5 (Authenticator Management)

Code Found:
┌─────────────────────────────────────────────────────────────
│ const config = {
│   clientId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
│   clientSecret: 'abc123secret456xyz'  // <-- VIOLATION
│ };
└─────────────────────────────────────────────────────────────

Issue:
Client secrets must never be stored in source code. This violates
ITSG-33 IA-5 (Authenticator Management) and TBS Standard on Security
Tabs. Exposed secrets can lead to unauthorized access to the identity
provider and impersonation attacks.

Recommended Action:
1. Remove the secret from source code immediately
2. Store in environment variable:
   - process.env.AZURE_CLIENT_SECRET (Node.js)
   - os.environ['AZURE_CLIENT_SECRET'] (Python)
3. For production: Use Azure Key Vault or equivalent secrets manager
4. CRITICAL: Rotate the exposed secret in Entra ID immediately

Remediation Example:
┌─────────────────────────────────────────────────────────────
│ const config = {
│   clientId: process.env.AZURE_CLIENT_ID,
│   clientSecret: process.env.AZURE_CLIENT_SECRET
│ };
└─────────────────────────────────────────────────────────────

--------------------------------------------------------------------------------

8.5 Report Footer

================================================================================
COMPLIANCE SUMMARY
================================================================================

{If any FAIL}:
⛔ This codebase has CRITICAL authentication compliance issues that must
   be resolved before deployment. Address all [Auth Error] findings.

{If only WARN}:
⚠️  This codebase has authentication warnings that should be reviewed.
   Consider addressing warnings to improve security posture.

{If all PASS}:
✅ This codebase passes all Government of Canada authentication
   compliance checks. Continue to monitor for changes.

--------------------------------------------------------------------------------
Next Steps:
1. Address all ❌ Fail findings before proceeding
2. Review ⚠️ Warning findings with your security team
3. Re-run /gc-review-iam after fixes are applied
4. Document any accepted risks with justification

Disclaimer: This is an automated pattern-based review and does not constitute
a formal Security Assessment and Authorization (SA&A). Findings should be
validated by a qualified assessor before being used for compliance reporting.

For questions about GoC authentication standards, consult:
- CCCS Cyber Centre: https://cyber.gc.ca
- TBS Digital Standards: https://www.canada.ca/en/government/system/digital-government
================================================================================

Technology Stack Reference

Node.js / Express

Session configuration check:

// express-session
app.use(session({
  secret: process.env.SESSION_SECRET,  // Not hardcoded
  cookie: {
    httpOnly: true,    // Required
    secure: true,      // Required for HTTPS
    sameSite: 'strict', // Required
    maxAge: 28800000   // 8 hours max
  },
  resave: false,
  saveUninitialized: false
}));

Passport OIDC check:

// passport-azure-ad or passport-openidconnect
passport.use(new OIDCStrategy({
  identityMetadata: 'https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration',
  clientID: process.env.AZURE_CLIENT_ID,
  clientSecret: process.env.AZURE_CLIENT_SECRET,  // From env
  responseType: 'code',
  scope: ['openid', 'profile', 'email']  // Minimal scopes
}));

Next.js / Auth.js

NextAuth configuration check:

// app/api/auth/[...nextauth]/route.js or auth.config.js
export const authOptions = {
  providers: [
    AzureADProvider({
      clientId: process.env.AZURE_AD_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
      tenantId: process.env.AZURE_AD_TENANT_ID
    })
  ],
  session: {
    strategy: 'jwt',
    maxAge: 28800  // 8 hours
  },
  cookies: {
    sessionToken: {
      options: {
        httpOnly: true,
        sameSite: 'lax',
        secure: true
      }
    }
  }
};

Python / Flask

Flask session configuration:

# config.py or app.py
app.config.update(
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_SAMESITE='Strict',
    PERMANENT_SESSION_LIFETIME=timedelta(hours=8),
    SECRET_KEY=os.environ.get('SECRET_KEY')  # From env
)

Authlib OIDC check:

# OIDC client configuration
oauth = OAuth(app)
oauth.register(
    name='azure',
    client_id=os.environ.get('AZURE_CLIENT_ID'),
    client_secret=os.environ.get('AZURE_CLIENT_SECRET'),
    server_metadata_url='https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration',
    client_kwargs={'scope': 'openid profile email'}
)

.NET

Microsoft.Identity.Web configuration:

// Program.cs or Startup.cs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

builder.Services.Configure<CookieAuthenticationOptions>(
    CookieAuthenticationDefaults.AuthenticationScheme,
    options =>
    {
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Strict;
        options.ExpireTimeSpan = TimeSpan.FromHours(8);
    });

appsettings.json check (secrets should NOT be here):

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "from-env-or-keyvault",
    "ClientId": "from-env-or-keyvault",
    "ClientSecret": "NEVER-IN-CONFIG-FILE"
  }
}

ITSG-33 Control Mapping

CheckITSG-33 ControlDescription
3.1 Authorized IdPIA-2, IA-8Identification and Authentication (Organizational Users, Non-Organizational Users)
3.2 No Hardcoded SecretsIA-5Authenticator Management
3.3 Discovery EndpointSC-8, SC-23Transmission Confidentiality, Session Authenticity
4.1 Cookie FlagsSC-8, SC-23Transmission Confidentiality, Session Authenticity
4.2 Session TimeoutAC-12, SC-10Session Termination, Network Disconnect
4.3 Token StorageSC-28Protection of Information at Rest
5.1 Scope MinimizationAC-6Least Privilege
5.2 Server-side ClaimsAC-4, SC-8Information Flow, Transmission Confidentiality
6.1 Session ClearingAC-12Session Termination
6.2 Federated LogoutAC-12, IA-4Session Termination, Identifier Management
7.1 Server-side RBACAC-3, AC-6Access Enforcement, Least Privilege
7.2 Role IntegrityAC-3, SI-10Access Enforcement, Information Input Validation

Usage

# Run authentication review on current project
/gc-review-iam

# Review specific files
/gc-review-iam src/auth/**

# Review with strict mode (warnings become failures)
/gc-review-iam --strict

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

gc-review-security

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

gc-review-a11y

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

gc-review-bilingual

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

gc-review-branding

No summary provided by upstream source.

Repository SourceNeeds Review