Skip to content
rise.toml Schema

OAuth Extension

Rise’s OAuth extension makes Rise act as an OAuth/OIDC proxy between your application and an upstream provider such as Google, GitHub, Snowflake, or a custom SSO service.

OAuth providers usually require every allowed redirect URI to be registered ahead of time. A Rise project can have many URLs: production, staging, branch previews, merge request previews, custom domains, and localhost during development.

The OAuth extension gives the provider one stable callback URL:

https://<rise-url>/oidc/<project>/<extension>/callback

Rise receives the upstream callback and forwards the user back to the original app URL that started the flow.

App URL -> Rise authorize -> OAuth provider
|
App URL <- Rise callback <-------+
  • A stable provider callback URL per project and extension.
  • OAuth 2.0 authorization-code flow for backend applications.
  • PKCE support for browser applications and public clients.
  • Token refresh through the Rise token endpoint.
  • OIDC discovery and JWKS proxying when the upstream provider supports it.
  • Encrypted storage for upstream provider client secrets.
  • Local development support through rise run --project.

Rise is a proxy, not a session store for your application. After the code exchange, your application owns the upstream tokens and decides how to store them.

FlowBest forClient authenticationNotes
Authorization code with PKCEBrowser apps and public clientsclient_id plus code_verifierNo client secret in the browser. Uses RFC 7636 PKCE.
Authorization code with client secretBackend appsclient_id plus client_secretToken exchange happens server-side. Store resulting tokens in an HttpOnly session or backend store.
Refresh tokenBackend apps or trusted clientsclient_id plus client_secretProxies grant_type=refresh_token to the upstream provider.

The Rise client ID is deterministic:

{project-name}-{extension-name}

For project my-app and extension oauth-google, the Rise client ID is my-app-oauth-google.

First register an OAuth application with the upstream provider. Use the Rise callback URL as the provider redirect URI:

https://<rise-url>/oidc/<project>/<extension>/callback

Then encrypt the upstream provider client secret and create the extension:

Terminal window
ENCRYPTED=$(rise encrypt "your_client_secret_here")
rise extension create oauth-google -p my-app \
--type oauth \
--spec '{
"provider_name": "Google",
"description": "Sign in with Google",
"client_id": "123456789.apps.googleusercontent.com",
"client_secret_encrypted": "'"$ENCRYPTED"'",
"issuer_url": "https://accounts.google.com",
"scopes": ["openid", "email", "profile"]
}'

You can also encrypt via stdin:

Terminal window
echo "your_client_secret_here" | rise encrypt

The rise encrypt command is rate-limited to 100 requests per hour per user.

OIDC-compliant providers expose an OpenID configuration document, so Rise can discover the authorization, token, and JWKS endpoints from issuer_url.

FieldOIDC-compliant providerNon-OIDC provider
provider_nameRequiredRequired
client_idRequiredRequired
client_secret_encryptedRequiredRequired
issuer_urlRequiredRequired
authorization_endpointAuto-discoveredRequired
token_endpointAuto-discoveredRequired
scopesProvider-specificProvider-specific

Examples:

ProviderOIDC compliantissuer_urlauthorization_endpointtoken_endpointTypical scopes
GoogleYeshttps://accounts.google.comAuto-discoveredAuto-discoveredopenid, email, profile
DexYesYour Dex issuer URLAuto-discoveredAuto-discoveredopenid, email, profile, groups
Auth0Yeshttps://<tenant>.auth0.comAuto-discoveredAuto-discoveredopenid, email, profile
GitHubNohttps://github.comhttps://github.com/login/oauth/authorizehttps://github.com/login/oauth/access_tokenread:user, user:email
SnowflakeNoYour Snowflake account URLProvider-specific authorization endpointProvider-specific token endpointProvider-specific

For non-OIDC providers, add the manual endpoints to the same extension spec:

{
"provider_name": "GitHub",
"client_id": "Iv1.abc123...",
"client_secret_encrypted": "<encrypted>",
"issuer_url": "https://github.com",
"authorization_endpoint": "https://github.com/login/oauth/authorize",
"token_endpoint": "https://github.com/login/oauth/access_token",
"scopes": ["read:user", "user:email"]
}

Your application starts login by redirecting the user to Rise:

GET {RISE_ISSUER}/oidc/{project}/{extension}/authorize

For browser applications using PKCE, include:

code_challenge=<challenge>&code_challenge_method=S256

For local development or custom callback paths, include:

redirect_uri=http://localhost:3000/callback

After the upstream provider authenticates the user, Rise redirects back to your app with an authorization code:

https://my-app.example.com/callback?code=<authorization-code>&state=<state>

Your app exchanges that code at:

POST {RISE_ISSUER}/oidc/{project}/{extension}/token

Use client_secret for backend apps or code_verifier for PKCE clients.

rise run --project my-app injects the same OAuth extension environment variables your deployed app receives:

VariablePurpose
{EXTENSION}_CLIENT_IDRise client ID, for example OAUTH_GOOGLE_CLIENT_ID.
{EXTENSION}_CLIENT_SECRETRise client secret for confidential clients.
{EXTENSION}_ISSUERRise OIDC proxy URL for this extension.
RISE_ISSUERRise server URL used to build authorize and token URLs.

Rise allows redirect_uri values that point to localhost, so the same provider registration can support production, preview, and local development.

GET /oidc/{project}/{extension}/authorize

Query parameters:

ParameterRequiredDescription
code_challengePKCE clients onlyBase64url-encoded SHA-256 hash of the code verifier.
code_challenge_methodNoOnly S256 is supported.
redirect_uriNoWhere Rise should redirect after OAuth. Allows localhost and project domains.
stateNoApplication state passed through the OAuth flow.
GET /oidc/{project}/{extension}/callback?code=...&state=...

This is the callback URL registered with the upstream provider. Rise handles it and redirects to the app with a Rise authorization code.

POST /oidc/{project}/{extension}/token
Content-Type: application/x-www-form-urlencoded

Authorization-code parameters:

ParameterRequiredDescription
grant_typeYesauthorization_code
codeYesAuthorization code from the callback.
client_idYesRise client ID.
client_secretConfidential clientsRise client secret.
code_verifierPKCE clientsOriginal PKCE verifier.

Refresh-token parameters:

ParameterRequiredDescription
grant_typeYesrefresh_token
refresh_tokenYesRefresh token from a previous token response.
client_idYesRise client ID.
client_secretYesRise client secret.

Token responses use the RFC 6749 shape:

{
"access_token": "eyJhbGc...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGc...",
"scope": "email profile",
"id_token": "eyJhbGc..."
}

Rise exposes an OIDC discovery endpoint for each OAuth extension:

GET /oidc/{project}/{extension}/.well-known/openid-configuration

The response rewrites upstream URLs to Rise proxy URLs:

{
"issuer": "https://rise.example.com/oidc/my-app/oauth-google",
"authorization_endpoint": "https://rise.example.com/oidc/my-app/oauth-google/authorize",
"token_endpoint": "https://rise.example.com/oidc/my-app/oauth-google/token",
"jwks_uri": "https://rise.example.com/oidc/my-app/oauth-google/jwks"
}

JWKS is proxied from the upstream provider:

GET /oidc/{project}/{extension}/jwks
  • Upstream client secrets are encrypted at rest and never exposed to browser clients.
  • Authorization codes are single-use and expire after 5 minutes.
  • OAuth state tokens protect against CSRF and expire after 10 minutes.
  • PKCE proves that a public client initiated the authorization flow.
  • Secret validation uses constant-time comparison.
  • Applications own token storage and refresh behavior after exchange.

“Failed to resolve OAuth endpoints” or “No authorization_endpoint in spec or OIDC discovery”

  • For OIDC-compliant providers, check that issuer_url is correct and supports OIDC discovery.
  • For non-OIDC providers, set authorization_endpoint and token_endpoint.
  • Test discovery with curl {issuer_url}/.well-known/openid-configuration.

“Invalid issuer_url URL”

  • Use a valid HTTPS URL.
  • Avoid trailing slashes and paths unless the provider documents them as part of the issuer.

“Token exchange failed with status 400”

  • Verify client_id and client_secret_encrypted.
  • Check that the provider redirect URI matches the Rise callback URL.
  • Review provider logs for the upstream OAuth error.

“No cached state found for state token”

  • The state token may have expired. Restart the OAuth flow.

“Invalid or expired authorization code”

  • Authorization codes are single-use and expire after 5 minutes. Restart the OAuth flow.