# OIDC Relying-Party Authorization Request (`kingii98/oidc-relying-party-authorization-request-and-redirect-uri`) Actor

Checks that an app login redirect sends the expected redirect_uri, client_id, PKCE, state, and nonce to the advertised authorization endpoint.

- **URL**: https://apify.com/kingii98/oidc-relying-party-authorization-request-and-redirect-uri.md
- **Developed by:** [kingii98](https://apify.com/kingii98) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 entry checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## OIDC Relying-Party Authorization Request and Redirect-URI Consistency Gate

This Actor checks the login redirect of an app that uses OpenID Connect (OIDC). Use it after a change to a proxy, an ingress, TLS termination, a host name, or an `EXTERNAL_URL`-type setting.

For each login URL, the Actor sends one GET request. It does not follow redirects. It reads only the first 3xx response. It then compares the authorization request in the `Location` header with your expected values.

The Actor does not log in. It does not use credentials or secrets. It does not use a browser.

### What the Actor checks

| Check name | Failure condition |
| --- | --- |
| `first-hop` | The first response is not a 3xx redirect, or the redirect is not an authorization request (no `response_type` and no `client_id`). |
| `redirect-uri` | `redirect_uri` is not exactly equal to `expectedRedirectUri`. |
| `client-id` | `client_id` is not equal to `expectedClientId`. The check runs only if you set `expectedClientId`. |
| `authorize-endpoint` | The authorization URL differs from `authorization_endpoint` in the discovery document. |
| `discovery` | You set `issuer`, but the discovery document could not be read. |
| `pkce` | `requirePkce` is true and the request has no `S256` code challenge. |
| `state` | The request has no `state`. |
| `nonce` | The request has no `nonce`. |
| `fetch` | The request failed (timeout, DNS error, private target, and so on). |
| `input` | The entry in the input is not valid. |

A failed check gives `verdict: "fail"` in the dataset. The run itself still succeeds.

### Input

- `entries` (1 to 200): each entry has `loginUrl`, `expectedRedirectUri`, and optional `expectedClientId`, `hostVariants`, and `issuer`.
- `hostVariants` (maximum 3): a host name (for example `app2.example.org`) a `scheme://host` value (for example `http://app.example.org`), or a scheme only (`http`, `https`, or `http://`). The Actor replaces the scheme and host of `loginUrl` and keeps the path and query. A scheme-only value changes only the scheme and keeps the host and port of `loginUrl`. The expected values stay the same.
- `issuer`: the Actor fetches `<issuer>/.well-known/openid-configuration` once for each distinct issuer in the run.
- `requirePkce`: default `true`.
- `stateStoreName`: optional. The Actor saves the last parameters of each target in this key-value store. Each item then has `changeEvents[]` with `field`, `previous`, and `current`. The first run gives an empty list.

An empty input runs the default fixture: `https://example.com/login`. That page is not an OIDC login. The default run gives a `fail` verdict (`first-hop`) and the run succeeds.

### Output

One dataset item for each entry and host variant. Fields: `loginUrl`, `firstHopStatus`, `authorizeHost`, `authorizeEndpointMatchesDiscovery`, `redirectUri`, `redirectUriMatchesExpected`, `redirectUriScheme`, `clientIdMatches`, `pkceMethod` (`S256`, `plain`, or `none`), `hasState`, `hasNonce`, `scope`, `responseType`, `verdict`, `failedChecks`, and `changeEvents` (only if the state store is set). A failed request also has `error`.

The key-value store record `SUMMARY` has `checked`, `passed`, `failed`, `skipped`, `failCountByCheck`, and `stoppedByChargeLimit`.

### Pricing event

| Event name | When the Actor charges | Price (proposal) |
| --- | --- | --- |
| `entry-checked` | One time for each login entry URL and host variant that the Actor checks. The Actor writes one dataset item for it. | USD 0.004 |

The Actor does not charge for an entry that the input check rejects. If the maximum charge is reached, the Actor stops, and `SUMMARY` shows `stoppedByChargeLimit: true` and the number of skipped targets. The default maximum charge of USD 2.00 covers 500 checks. Increase it for large inputs.

### Safety limits

- The Actor accepts only `http` and `https` URLs without credentials.
- The Actor rejects a host that resolves to a private or reserved address (loopback, private, link-local, carrier-grade NAT, and others).
- It reads at most 1 MB from a response, with a timeout of 15 seconds.
- It follows no redirect. It runs at most 5 requests at the same time.

### Limits

- The Actor checks only leg 1 (the authorization request). It cannot find a mismatch that exists only in the token exchange (leg 2).
- The Actor checks the address once, before it connects. A DNS change between the check and the connection is not detected.
- If the app starts an OIDC login only after more than one redirect, the `first-hop` check fails.
- The Actor requires `nonce`, as the contract states. Some code-flow apps do not send a nonce. Treat that result as expected for such an app.

### Development

```
uv run pytest
uv run ruff check .
```

# Actor input Schema

## `entries` (type: `array`):

1 to 200 login entries. Each entry is an object with loginUrl and expectedRedirectUri. Optional keys: expectedClientId, hostVariants (maximum 3 alternate hosts, scheme://host values, or a scheme only such as http, which keeps the host and port of loginUrl), issuer.

## `requirePkce` (type: `boolean`):

When true, an entry fails if the request has no S256 code challenge.

## `stateStoreName` (type: `string`):

Optional. Name of a key-value store. When set, the Actor saves the last parameters of each target and reports changes in changeEvents.

## Actor input object example

```json
{
  "entries": [
    {
      "loginUrl": "https://example.com/login",
      "expectedRedirectUri": "https://example.com/callback"
    }
  ],
  "requirePkce": true
}
```

# Actor output Schema

## `results` (type: `string`):

One item per login entry and host variant.

## `summary` (type: `string`):

Counts of checked, passed, and failed targets, and the fail count by check.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "entries": [
        {
            "loginUrl": "https://example.com/login",
            "expectedRedirectUri": "https://example.com/callback"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kingii98/oidc-relying-party-authorization-request-and-redirect-uri").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "entries": [{
            "loginUrl": "https://example.com/login",
            "expectedRedirectUri": "https://example.com/callback",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("kingii98/oidc-relying-party-authorization-request-and-redirect-uri").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "entries": [
    {
      "loginUrl": "https://example.com/login",
      "expectedRedirectUri": "https://example.com/callback"
    }
  ]
}' |
apify call kingii98/oidc-relying-party-authorization-request-and-redirect-uri --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kingii98/oidc-relying-party-authorization-request-and-redirect-uri"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/btv6bBOal8uXoNIsa/builds/hBAULc4sFOFENlIAj/openapi.json
