Microsoft Entra Sign-in & Audit Logs Tool
Read Entra sign-in logs, directory audit logs and provisioning events. For a security review, a failed-sign-in sweep, or "who added this person to that group".
Tool ID
msgraph-signin-audit
Credential Required
Yes — the shared msgraph credential, owned by the msgraph connector. Configure the tenant
once and all nine tools use it; rotating the client secret is one edit rather than nine.
It is a server-managed OAuth credential: InTouch mints and refreshes the access token itself and
the connector never sees the secret. For unattended work set grant_type to client_credentials
(the app-only grant) — an app-only token belongs to the application, so a 06:00 schedule does not
run as a person, see only what that person can see, and stop working the day they leave.
Credential Properties
| Property | Type | Default | Description |
|---|---|---|---|
client_id |
string | — | Required. The Entra app registration's Application (client) ID. |
client_secret |
string | — | Required for app-only. The secret's Value, not its Secret ID. |
token_url |
string | — | Required. https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token |
grant_type |
string | (blank) | client_credentials for unattended app-only use. Blank means the user-delegated refresh-token flow. |
scope |
string | — | https://graph.microsoft.com/.default — resolves to whatever application permissions an admin has consented to. |
access_token |
string | — | Machine-managed. Leave empty; the server mints and persists it. |
refresh_token |
string | — | Delegated credentials only. Not used by the app-only grant. |
expires_at |
string | 0 |
Machine-managed expiry, epoch seconds. |
Entra setup
- https://entra.microsoft.com → App registrations → your app → API permissions.
- Add a permission → Microsoft Graph → Application permissions — not Delegated. Picking Delegated is the most common failure: it consents cleanly and then 403s at runtime.
- Tick the permissions listed under Permissions below, then Grant admin consent. The Status column must read Granted.
- Permissions live in the token, not the portal. A token minted before the grant carries the old
claims for about an hour — blank
access_tokento force a fresh mint.
Permissions
AuditLog.Read.All.
Operations
1. list_sign_ins — List sign-ins
Who authenticated, from where, and whether it succeeded. Requires Entra ID P1 or P2. Calls GET /auditLogs/signIns.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. list_sign_ins |
query |
string | — | Optional. OData query params as a JSON object string. ALWAYS bound a listing by time — {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z", "$top": 200}. Failed sign-ins only: {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z and status/errorCode ne 0"}. Entra retains roughly 7 days without a premium tier, 30 days with one — older records do not exist to be queried. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
2. get_sign_in — Get one sign-in
One sign-in record by id. Calls GET /auditLogs/signIns/{id}.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. get_sign_in |
id |
string | — | Required. Log record id. Required by get_sign_in and get_directory_audit. |
query |
string | — | Optional. OData query params as a JSON object string. ALWAYS bound a listing by time — {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z", "$top": 200}. Failed sign-ins only: {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z and status/errorCode ne 0"}. Entra retains roughly 7 days without a premium tier, 30 days with one — older records do not exist to be queried. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
3. list_directory_audits — List directory changes
Who changed what — role assignments, group membership, app consent. No premium tier needed. Calls GET /auditLogs/directoryAudits.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. list_directory_audits |
query |
string | — | Optional. OData query params as a JSON object string. ALWAYS bound a listing by time — {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z", "$top": 200}. Failed sign-ins only: {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z and status/errorCode ne 0"}. Entra retains roughly 7 days without a premium tier, 30 days with one — older records do not exist to be queried. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
4. get_directory_audit — Get one audit record
One directory audit record by id. Calls GET /auditLogs/directoryAudits/{id}.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. get_directory_audit |
id |
string | — | Required. Log record id. Required by get_sign_in and get_directory_audit. |
query |
string | — | Optional. OData query params as a JSON object string. ALWAYS bound a listing by time — {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z", "$top": 200}. Failed sign-ins only: {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z and status/errorCode ne 0"}. Entra retains roughly 7 days without a premium tier, 30 days with one — older records do not exist to be queried. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
5. list_provisioning — List provisioning events
Events from Entra's user-sync connectors. Calls GET /auditLogs/provisioning.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. list_provisioning |
query |
string | — | Optional. OData query params as a JSON object string. ALWAYS bound a listing by time — {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z", "$top": 200}. Failed sign-ins only: {"$filter": "createdDateTime ge 2026-08-20T00:00:00Z and status/errorCode ne 0"}. Entra retains roughly 7 days without a premium tier, 30 days with one — older records do not exist to be queried. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
Paging
Graph answers a collection with 100 rows and an @odata.nextLink, so a naive listing returns a
fraction of a large tenant with HTTP 200 and no sign anything is missing. This connector follows
the continuation link until the collection is exhausted. If it stops at the page cap it says so —
the result carries complete: false with a warning, and the step returns WARNING rather than
SUCCESS. A page cap must never look like the end of the data.
Notes — two things that are not bugs
The two logs have different entitlements, which is why they are separate operations.
list_sign_ins needs Entra ID P1 or P2; on a tenant without it the endpoint answers 403
forever, consent or no consent. list_directory_audits — the record of directory changes, such
as a role assignment or a group membership edit — is available without a premium tier. So on a bare
tenant this connector is half useful rather than useless, and it matters which half you ask for.
Retention is days, not years, and Graph is not an archive. Entra keeps roughly 7 days without a premium tier and 30 with one. "Every failed sign-in last quarter" has no answer here at any permission level — the tenant had to be exporting to Log Analytics, storage or a SIEM before the question was asked. Scheduling a nightly job through this connector is precisely how an organisation builds that history for itself.
Always bound the query. Sign-in logs are the highest-volume collection in Graph; an unfiltered
listing pages until it hits the cap and reports complete: false, having spent minutes and a chunk
of the tenant's throttling budget to return a prefix.
{"$filter": "createdDateTime ge 2026-08-20T00:00:00Z", "$top": 200} — every time.
Self-Contained JAR
The jar bundles all runtime dependencies; only intouch-tool-api is provided by the server.
Publishes
The keys a later task may reference as {{taskName.key}}. Referencing anything not listed here resolves to nothing at run time.
completeoperationresultstatuswarning