Skip to content

feat: machine auth eligibility API - #3

Open
raphapr wants to merge 6 commits into
mainfrom
feat/machine-auth-eligibility-api
Open

feat: machine auth eligibility API#3
raphapr wants to merge 6 commits into
mainfrom
feat/machine-auth-eligibility-api

Conversation

@raphapr

@raphapr raphapr commented Apr 23, 2026

Copy link
Copy Markdown

Summary

Adds machine-to-machine authentication support for querying user eligibility, enabling machine user for access requests.

Motivation

The existing createRequestOnBehalf mutation allows machine clients to submit access requests, but they couldn't discover what accounts/permissions a user is eligible for. This PR adds the missing query capability.

Changes

  • getEligibilityForUser(email: String!) query — New Lambda that looks up a user by email in Identity Center, retrieves their group memberships, and returns all eligible accounts, permissions, max duration, and approval requirements
  • getPermissions machine auth — Added api/admin scope to existing query so machine clients can list permission sets
  • IAM fix — Added sso:ListInstances permission (AWS uses both sso: and sso-admin: prefixes)

Summary by CodeRabbit

  • New Features
    • Added user eligibility lookup endpoint that returns available accounts and permissions for specified users, including maximum access duration and approval status.
    • Expanded permissions query authorization to grant access to additional user groups.

raphapr added 3 commits April 23, 2026 16:33
- New Lambda function to get user eligibility by email
- Returns accounts, permissions, maxDuration, approvalRequired
- Supports machine auth via api/admin scope
- Also adds getPermissions machine auth support
@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: loadsmart/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 95689e36-79be-4fd4-9de7-670354818aeb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Introduces a new GraphQL query getEligibilityForUser that determines user eligibility by email. A new Lambda function integrates with AWS SSO/Identity Store to fetch group memberships, loads entitlements from DynamoDB, merges results into deduplicated account and permission options, and expands organizational units to concrete accounts. The getPermissions query authorization is also expanded to include admin group access.

Changes

Cohort / File(s) Summary
GraphQL Schema
amplify/backend/api/team/schema.graphql
Added getEligibilityForUser(email: String!): EligibilityResult query with admin scope authorization. Expanded getPermissions auth rules to include group-based admin access. Introduced three new types: EligibilityResult, AccountOption, and PermissionOption for structured eligibility data.
Backend Configuration
amplify/backend/backend-config.json
Registered new Lambda function resource teamgetEligibilityForUser with CloudFormation provider, configured build settings, and explicit dependency on the team AppSync API via GraphQLAPIIdOutput.
Lambda Function Configuration
amplify/backend/function/teamgetEligibilityForUser/function-parameters.json
Defined permissions mapping to reference DynamoDB eligibility table via CloudFormation GetAtt attribute.
Lambda Implementation
amplify/backend/function/teamgetEligibilityForUser/src/index.py
Implemented handler and supporting functions for email-based user eligibility lookup. Integrates with AWS SSO Instance, Identity Store for user/group resolution, DynamoDB for entitlement data retrieval, and AWS Organizations for OU expansion. Merges and deduplicates entitlements into account/permission lists with aggregated maxDuration and approvalRequired flags.
Lambda Infrastructure
amplify/backend/function/teamgetEligibilityForUser/teamgetEligibilityForUser-cloudformation-template.json
CloudFormation template provisioning Python 3.10 Lambda (arm64) with execution role, environment variables, and IAM policies. Grants permissions for CloudWatch Logs, DynamoDB GetItem, and read/list access to SSO, Identity Store, and Organizations services.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant AppSync as AppSync<br/>(GraphQL)
    participant Lambda as Lambda<br/>(teamgetEligibilityForUser)
    participant SSO as AWS SSO<br/>Instance
    participant IdentityStore as Identity Store<br/>(User/Groups)
    participant DynamoDB as DynamoDB<br/>(Entitlements)
    participant Orgs as AWS Organizations<br/>(OUs→Accounts)

    User->>AppSync: getEligibilityForUser(email)
    AppSync->>Lambda: invoke with email
    
    Lambda->>SSO: get_sso_instance()
    SSO-->>Lambda: Identity Store ID
    
    Lambda->>IdentityStore: get_user_by_email(email)
    IdentityStore-->>Lambda: UserId
    
    Lambda->>IdentityStore: list_group_memberships(UserId)
    IdentityStore-->>Lambda: [GroupIds]
    
    Lambda->>DynamoDB: get_entitlements(UserId)
    DynamoDB-->>Lambda: entitlements_user
    
    loop For each GroupId
        Lambda->>DynamoDB: get_entitlements(GroupId)
        DynamoDB-->>Lambda: entitlements_group
    end
    
    loop For each OU in entitlements
        Lambda->>Orgs: list_accounts_for_ou(OU)
        Orgs-->>Lambda: [AccountIds]
    end
    
    Lambda->>Lambda: merge_eligibility(all_entitlements)
    Lambda-->>AppSync: EligibilityResult
    AppSync-->>User: {accounts, permissions, maxDuration, approvalRequired}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

security-change

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'feat: machine auth eligibility API' directly and accurately summarizes the main change: adding a new eligibility API for machine authentication with a GraphQL query backed by a Lambda function.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/machine-auth-eligibility-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@raphapr raphapr changed the title feat: Machine auth eligibility API feat: machine auth eligibility API Apr 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
amplify/backend/function/teamgetEligibilityForUser/src/index.py (1)

132-134: Normalize email before lookup.

Identity Center email lookup via emails.value is typically case-sensitive on stored value. Consider trimming and lower-casing the input to match common provisioning normalization, and validating minimally that it looks like an email before calling downstream APIs.

-    email = event.get('arguments', {}).get('email')
-    if not email:
-        raise Exception("Email is required")
+    email = (event.get('arguments', {}).get('email') or '').strip().lower()
+    if not email or '@' not in email:
+        raise Exception("A valid email is required")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@amplify/backend/function/teamgetEligibilityForUser/src/index.py` around lines
132 - 134, Normalize and validate the provided email before performing the
lookup: in the handler where you read the argument into the email variable
(email = event.get('arguments', {}).get('email')), trim whitespace and convert
to lowercase (e.g., email = email.strip().lower()), then perform a minimal
validation (ensure it contains an '@' and a domain portion) and raise the
existing "Email is required" or a validation Exception if it fails; use the
normalized email for all downstream calls to Identity Center/teams lookup to
avoid case-sensitivity mismatches.
amplify/backend/function/teamgetEligibilityForUser/teamgetEligibilityForUser-cloudformation-template.json (1)

197-219: Verify Identity Store/Organizations scoping.

sso:ListInstances, sso-admin:ListInstances, identitystore:*, and organizations:ListAccountsForParent don't support resource-level ARNs, so "*" is correct. Consider adding an aws:ResourceAccount condition on the Organizations action to pin to the management account if this Lambda runs outside it, but not a blocker for the current deployment model.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@amplify/backend/function/teamgetEligibilityForUser/teamgetEligibilityForUser-cloudformation-template.json`
around lines 197 - 219, The policy allows "organizations:ListAccountsForParent"
with "Resource": "*" — add a condition to scope it to the management account by
including an "Condition" block (e.g., "StringEquals": {"aws:ResourceAccount":
"<management-account-id>"} ) on the statement that contains the
"organizations:ListAccountsForParent" action; leave the SSO and Identity Store
statements as-is since they require "*" resources. Locate the statement
containing the "organizations:ListAccountsForParent" action and add the
Condition there, replacing "<management-account-id>" with the actual management
account ID.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@amplify/backend/function/teamgetEligibilityForUser/src/index.py`:
- Around line 13-17: The get_sso_instance function currently assumes
response['Instances'][0] exists and will raise IndexError if Identity Center
isn't configured; update get_sso_instance to check that 'Instances' exists and
is non-empty on the response before indexing, and if empty raise a clear,
explicit exception (e.g., ValueError or a custom exception) with a descriptive
message like "No Identity Center (SSO) instances found in this account/region"
or return an explicit None; ensure callers handle this new error/None and keep
the boto3 client/list_instances call and function name get_sso_instance
unchanged.
- Around line 96-103: The current int(entitlement.get('duration', 0)) is brittle
and can raise for None or non-numeric strings; change to defensively coerce
duration from entitlement: pull raw = entitlement.get('duration'), treat
None/empty as 0, then try converting with int(raw), falling back to
int(float(raw)) in case of decimal strings, and catch ValueError/TypeError to
skip/ignore malformed values (optionally log a warning) before
comparing/updating max_duration; leave the approvalRequired check
(entitlement.get('approvalRequired', True)) as-is. Ensure you update the code
that references entitlement, duration, and max_duration accordingly.
- Around line 129-147: Logs currently print raw PII (email) in the handler;
replace any direct prints of the event and email (the print call and the line
that prints f"User {email} is in groups...") with non-PII identifiers such as
the resolved user_id from get_user_by_email or a one-way hash/truncated digest
of the email (e.g., sha256 and first N chars) before logging; ensure you still
log useful context like group_ids and use the functions get_sso_instance,
get_user_by_email, and list_group_memberships to obtain and log only the safe
identifier.
- Around line 67-82: list_accounts_for_ou currently only returns direct-child
accounts; update it to recursively traverse child OUs using
organizations:list_organizational_units_for_parent and aggregate accounts from
each OU (call list_accounts_for_parent for each OU and recurse into nested OUs)
so nested accounts are included; also ensure the IAM policy (e.g., the
teamPublishOUs/CloudFormation role) grants
organizations:ListOrganizationalUnitsForParent (or organizations:List* as used
elsewhere) so the recursive calls succeed.

---

Nitpick comments:
In `@amplify/backend/function/teamgetEligibilityForUser/src/index.py`:
- Around line 132-134: Normalize and validate the provided email before
performing the lookup: in the handler where you read the argument into the email
variable (email = event.get('arguments', {}).get('email')), trim whitespace and
convert to lowercase (e.g., email = email.strip().lower()), then perform a
minimal validation (ensure it contains an '@' and a domain portion) and raise
the existing "Email is required" or a validation Exception if it fails; use the
normalized email for all downstream calls to Identity Center/teams lookup to
avoid case-sensitivity mismatches.

In
`@amplify/backend/function/teamgetEligibilityForUser/teamgetEligibilityForUser-cloudformation-template.json`:
- Around line 197-219: The policy allows "organizations:ListAccountsForParent"
with "Resource": "*" — add a condition to scope it to the management account by
including an "Condition" block (e.g., "StringEquals": {"aws:ResourceAccount":
"<management-account-id>"} ) on the statement that contains the
"organizations:ListAccountsForParent" action; leave the SSO and Identity Store
statements as-is since they require "*" resources. Locate the statement
containing the "organizations:ListAccountsForParent" action and add the
Condition there, replacing "<management-account-id>" with the actual management
account ID.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: loadsmart/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0db32849-27b4-4e5f-ad5e-976bf79f4af9

📥 Commits

Reviewing files that changed from the base of the PR and between 8ed6364 and 7a6131c.

📒 Files selected for processing (5)
  • amplify/backend/api/team/schema.graphql
  • amplify/backend/backend-config.json
  • amplify/backend/function/teamgetEligibilityForUser/function-parameters.json
  • amplify/backend/function/teamgetEligibilityForUser/src/index.py
  • amplify/backend/function/teamgetEligibilityForUser/teamgetEligibilityForUser-cloudformation-template.json

Comment on lines +13 to +17
def get_sso_instance():
"""Get SSO instance details"""
client = boto3.client('sso-admin')
response = client.list_instances()
return response['Instances'][0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against empty Instances list.

response['Instances'][0] raises IndexError (surfaced to the caller as a 500) if Identity Center is not configured in the account/region. A clearer error message aids operator debugging.

🛡️ Proposed fix
 def get_sso_instance():
     """Get SSO instance details"""
     client = boto3.client('sso-admin')
     response = client.list_instances()
-    return response['Instances'][0]
+    instances = response.get('Instances') or []
+    if not instances:
+        raise Exception("No IAM Identity Center instance found in this account/region")
+    return instances[0]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def get_sso_instance():
"""Get SSO instance details"""
client = boto3.client('sso-admin')
response = client.list_instances()
return response['Instances'][0]
def get_sso_instance():
"""Get SSO instance details"""
client = boto3.client('sso-admin')
response = client.list_instances()
instances = response.get('Instances') or []
if not instances:
raise Exception("No IAM Identity Center instance found in this account/region")
return instances[0]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@amplify/backend/function/teamgetEligibilityForUser/src/index.py` around lines
13 - 17, The get_sso_instance function currently assumes
response['Instances'][0] exists and will raise IndexError if Identity Center
isn't configured; update get_sso_instance to check that 'Instances' exists and
is non-empty on the response before indexing, and if empty raise a clear,
explicit exception (e.g., ValueError or a custom exception) with a descriptive
message like "No Identity Center (SSO) instances found in this account/region"
or return an explicit None; ensure callers handle this new error/None and keep
the boto3 client/list_instances call and function name get_sso_instance
unchanged.

Comment on lines +67 to +82
def list_accounts_for_ou(ou_id):
"""List all accounts in an OU"""
client = boto3.client('organizations')
accounts = []
try:
paginator = client.get_paginator('list_accounts_for_parent')
pages = paginator.paginate(ParentId=ou_id)
for page in pages:
for account in page.get('Accounts', []):
accounts.append({
'id': account['Id'],
'name': account['Name']
})
except ClientError as e:
print(f"Error listing accounts for OU {ou_id}: {e}")
return accounts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether other Lambdas in the repo already implement OU→accounts expansion,
# to see what the established behavior/contract is for `ous` in Eligibility records.
rg -nP -C3 '\blist_accounts_for_parent\b|\blist_organizational_units_for_parent\b'
rg -nP -C3 "'ous'|\"ous\"" --type=py

Repository: loadsmart/iam-identity-center-team

Length of output: 5685


🏁 Script executed:

#!/bin/bash
# Check CloudFormation templates for current policy grants
fd -e yaml -e yml -e json | xargs grep -l "Organizations\|organizations" | head -20

Repository: loadsmart/iam-identity-center-team

Length of output: 1612


🏁 Script executed:

#!/bin/bash
# Search for CloudFormation policy definitions related to organizations
fd -e yaml -e yml -e json | xargs grep -l "organizations\|ListAccounts\|ListOrganizational" 2>/dev/null

Repository: loadsmart/iam-identity-center-team

Length of output: 1530


🏁 Script executed:

#!/bin/bash
# Look for comments or TODOs about OU recursion or nested structures
rg -i "nested|recursiv|ou.*tree|organizational.*unit" --type=py -B2 -A2

Repository: loadsmart/iam-identity-center-team

Length of output: 2406


🏁 Script executed:

#!/bin/bash
# Check the policy for teamgetEligibilityForUser
cat amplify/backend/function/teamgetEligibilityForUser/custom-policies.json 2>/dev/null || echo "File not found"

Repository: loadsmart/iam-identity-center-team

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Check the policy for teamPublishOUs which has the recursive implementation
cat amplify/backend/function/teamPublishOUs/custom-policies.json

Repository: loadsmart/iam-identity-center-team

Length of output: 193


🏁 Script executed:

#!/bin/bash
# Also check the main CF template for teamgetEligibilityForUser
cat amplify/backend/function/teamgetEligibilityForUser/teamgetEligibilityForUser-cloudformation-template.json

Repository: loadsmart/iam-identity-center-team

Length of output: 6202


list_accounts_for_ou does not recurse into nested OUs.

organizations:list_accounts_for_parent returns only accounts that are direct children of the given parent; accounts nested under child OUs are not included. When Eligibility records reference parent OUs with nested structures, users will silently receive an incomplete account list. This is a systemic issue affecting teamgetEligibilityForUser, teamRouter, and teamgetEntitlement — all use the same non-recursive pattern.

Consider implementing recursion via list_organizational_units_for_parent and aggregating results, or document that only direct-child accounts are supported. If recursing, grant organizations:ListOrganizationalUnitsForParent in the CloudFormation policy (example: teamPublishOUs grants both permissions via wildcard organizations:List*).

♻️ Sketch of a recursive expansion
 def list_accounts_for_ou(ou_id):
-    """List all accounts in an OU"""
+    """Recursively list all accounts in an OU (including nested OUs)."""
     client = boto3.client('organizations')
     accounts = []
     try:
-        paginator = client.get_paginator('list_accounts_for_parent')
-        pages = paginator.paginate(ParentId=ou_id)
-        for page in pages:
+        acc_pages = client.get_paginator('list_accounts_for_parent').paginate(ParentId=ou_id)
+        for page in acc_pages:
             for account in page.get('Accounts', []):
                 accounts.append({'id': account['Id'], 'name': account['Name']})
+        ou_pages = client.get_paginator('list_organizational_units_for_parent').paginate(ParentId=ou_id)
+        for page in ou_pages:
+            for child in page.get('OrganizationalUnits', []):
+                accounts.extend(list_accounts_for_ou(child['Id']))
     except ClientError as e:
         print(f"Error listing accounts for OU {ou_id}: {e}")
     return accounts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@amplify/backend/function/teamgetEligibilityForUser/src/index.py` around lines
67 - 82, list_accounts_for_ou currently only returns direct-child accounts;
update it to recursively traverse child OUs using
organizations:list_organizational_units_for_parent and aggregate accounts from
each OU (call list_accounts_for_parent for each OU and recurse into nested OUs)
so nested accounts are included; also ensure the IAM policy (e.g., the
teamPublishOUs/CloudFormation role) grants
organizations:ListOrganizationalUnitsForParent (or organizations:List* as used
elsewhere) so the recursive calls succeed.

Comment on lines +96 to +103
# Track max duration
duration = int(entitlement.get('duration', 0))
if duration > max_duration:
max_duration = duration

# If any entitlement doesn't require approval, set to False
if not entitlement.get('approvalRequired', True):
approval_required = False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

int(duration) is fragile against None / non-numeric values.

If a DynamoDB item stores duration as null or a non-integer string (e.g. ISO-8601 "PT4H" or a decimal), int(...) raises and the whole query fails. The schema types Eligibility.duration as String, so any string shape is accepted on write. Coerce defensively:

🛡️ Proposed fix
-        # Track max duration
-        duration = int(entitlement.get('duration', 0))
-        if duration > max_duration:
-            max_duration = duration
+        # Track max duration, tolerating missing / non-numeric values.
+        raw_duration = entitlement.get('duration') or 0
+        try:
+            duration = int(raw_duration)
+        except (TypeError, ValueError):
+            print(f"Ignoring non-numeric duration: {raw_duration!r}")
+            duration = 0
+        if duration > max_duration:
+            max_duration = duration
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@amplify/backend/function/teamgetEligibilityForUser/src/index.py` around lines
96 - 103, The current int(entitlement.get('duration', 0)) is brittle and can
raise for None or non-numeric strings; change to defensively coerce duration
from entitlement: pull raw = entitlement.get('duration'), treat None/empty as 0,
then try converting with int(raw), falling back to int(float(raw)) in case of
decimal strings, and catch ValueError/TypeError to skip/ignore malformed values
(optionally log a warning) before comparing/updating max_duration; leave the
approvalRequired check (entitlement.get('approvalRequired', True)) as-is. Ensure
you update the code that references entitlement, duration, and max_duration
accordingly.

Comment on lines +129 to +147
print(f"Received event: {event}")

# Extract email from arguments
email = event.get('arguments', {}).get('email')
if not email:
raise Exception("Email is required")

# Get SSO instance
sso_instance = get_sso_instance()
identity_store_id = sso_instance['IdentityStoreId']

# Look up user by email
user_id = get_user_by_email(identity_store_id, email)
if not user_id:
raise Exception(f"User with email '{email}' not found in Identity Center")

# Get user's group memberships
group_ids = list_group_memberships(identity_store_id, user_id)
print(f"User {email} is in groups: {group_ids}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid logging user email (PII).

print(f"Received event: {event}") and print(f"User {email} is in groups: {group_ids}") emit the user's email to CloudWatch Logs. This persists PII in log storage and can trigger GDPR/CCPA retention obligations. Log a hashed/truncated identifier or just the user_id after lookup.

🔒 Proposed fix
-    print(f"Received event: {event}")
+    # Avoid logging full event (contains user email).
+    print("Received getEligibilityForUser event")
@@
-    # Get user's group memberships
     group_ids = list_group_memberships(identity_store_id, user_id)
-    print(f"User {email} is in groups: {group_ids}")
+    print(f"User {user_id} is in {len(group_ids)} group(s)")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@amplify/backend/function/teamgetEligibilityForUser/src/index.py` around lines
129 - 147, Logs currently print raw PII (email) in the handler; replace any
direct prints of the event and email (the print call and the line that prints
f"User {email} is in groups...") with non-PII identifiers such as the resolved
user_id from get_user_by_email or a one-way hash/truncated digest of the email
(e.g., sha256 and first N chars) before logging; ensure you still log useful
context like group_ids and use the functions get_sso_instance,
get_user_by_email, and list_group_memberships to obtain and log only the safe
identifier.

- Add email format validation (regex check)
- Change error message to generic to prevent user enumeration
- Remove logging of email, group IDs, and eligibility results
@raphapr
raphapr force-pushed the feat/machine-auth-eligibility-api branch from 9aba28e to a5c83f3 Compare April 23, 2026 19:53
raphapr added 2 commits April 23, 2026 16:54
- Guard against empty Instances list in get_sso_instance()
- Defensive coercion for duration (handles None/non-numeric)
- Replace event logging with generic message
- Log user_id instead of email
- Log counts instead of full eligibility result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant