georest.esri.auth

Sign in to ArcGIS Online or ArcGIS Enterprise (Portal) as a user.

Once a session exists, every portal and services call that is not given an explicit token= sends the session’s token, but only to hosts the session covers: the portal itself, *.arcgis.com for ArcGIS Online, the portal’s federated servers, and anything added with trust_host(). A public service on another host never receives it.

Quick start:

from georest.esri import auth

# OAuth 2.0 (authorization code + PKCE). Works with enterprise SSO
# (SAML / eAuth) because the login happens in the browser.
auth.login("https://myorg.maps.arcgis.com", client_id="abc123")

# Or hand in a token from somewhere else, e.g. ArcGIS Pro via the
# arcgis API (run inside Pro's Python environment):
from arcgis.gis import GIS
gis = GIS("Pro")
auth.set_token(gis._con.token, portal=gis.url,
               refresh=lambda: gis._con.token)

auth.status()     # portal, user, expiry - never the token
auth.logout()

For a long-running process such as an MCP server, sign in once from a terminal with georest-login (which calls login(persist=True)). The server then picks the saved session up on its first request; nothing about credentials ever appears in a tool schema.

One-time setup: register an app in the org (Content > New item > Developer credentials > OAuth 2.0 credentials), add the redirect URL http://127.0.0.1:8765/callback, and use its client id. PKCE needs no client secret.

Copyright 2026 Ryan Rock and Ian Housman

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Module Attributes

CLIENT_ID_ENV

Environment variables read when no argument is given.

TOKEN_ENV

A raw token, used only when no session exists.

CREDENTIALS_PATH

Where login(persist=True) keeps the refresh token.

Functions

credentials_for(url)

The session token and extra headers to send to url, if any.

login([portal, client_id, port, ...])

Sign in through the browser with OAuth 2.0 authorization code + PKCE.

logout()

End the session and delete any saved credentials.

restore([path])

Load a session saved by login(persist=True).

set_token(token[, portal, expires, referer, ...])

Use a token obtained elsewhere, e.g. from the arcgis API's GIS.

status()

Describe the active session, or None.

trust_host(host)

Allow the session token to be sent to host as well.

georest.esri.auth.CLIENT_ID_ENV = 'GEOREST_ESRI_CLIENT_ID'

Environment variables read when no argument is given.

georest.esri.auth.TOKEN_ENV = 'GEOREST_ESRI_TOKEN'

A raw token, used only when no session exists. Needs PORTAL_ENV too, which decides the hosts it may be sent to.

georest.esri.auth.CREDENTIALS_PATH = WindowsPath('C:/Users/ianho/.georest/esri_credentials.json')

Where login(persist=True) keeps the refresh token.

georest.esri.auth.login(portal: str | None = None, client_id: str | None = None, *, port: int = 8765, open_browser: bool = True, persist: bool = False, timeout: float = 300) → dict[str, Any][source]

Sign in through the browser with OAuth 2.0 authorization code + PKCE.

Parameters:
  • portal – Portal URL (an org URL such as https://myorg.maps.arcgis.com shows the org’s SSO button directly) or a short name from portal.PORTALS. Defaults to $GEOREST_ESRI_PORTAL.

  • client_id – The registered app’s client id. Defaults to $GEOREST_ESRI_CLIENT_ID.

  • port – Local port for the redirect. The app must list http://127.0.0.1:<port>/callback as a redirect URL.

  • open_browser – Open the sign-in page automatically. The URL is always printed to stderr as well.

  • persist – Save the refresh token to CREDENTIALS_PATH so later processes sign in without a browser.

  • timeout – Seconds to wait for the sign-in to finish.

Returns:

status() for the new session.

Raises:

Example:

from georest.esri import auth

# Opens a browser to sign in; enterprise single sign-on works.
auth.login("https://myorg.maps.arcgis.com", client_id="YOUR_CLIENT_ID",
           persist=True)   # persist=True keeps you signed in across sessions
georest.esri.auth.set_token(token: str, portal: str | None = None, *, expires: Any = None, referer: str | None = None, refresh: Callable[[], str | tuple[str, Any]] | None = None) → dict[str, Any][source]

Use a token obtained elsewhere, e.g. from the arcgis API’s GIS.

Parameters:
  • token – The access token.

  • portal – The portal it was issued by (URL or short name). Defaults to $GEOREST_ESRI_PORTAL.

  • expires – When it expires: epoch seconds, epoch milliseconds, or a datetime. Unknown by default.

  • referer – Sent as the Referer header, for tokens bound to one.

  • refresh – Optional zero-argument callable returning a fresh token (or (token, expires)). Called shortly before expires, and once when a request is rejected as an invalid token (Esri code 498).

Returns:

status() for the new session.

Example:

from arcgis.gis import GIS          # in ArcGIS Pro's Python
from georest.esri import auth

gis = GIS("Pro")
auth.set_token(gis._con.token, portal=gis.url, refresh=lambda: gis._con.token)
georest.esri.auth.restore(path: str | PathLike | None = None) → dict[str, Any] | None[source]

Load a session saved by login(persist=True).

Called automatically on the first request when no session exists, so a process started after georest-login needs no code at all. The access token is fetched on first use.

Returns:

status(), or None if nothing usable is saved.

Example:

from georest.esri import auth

# Pick up a session saved earlier by login(persist=True) or georest-login
auth.restore()
georest.esri.auth.logout() → None[source]

End the session and delete any saved credentials.

$GEOREST_ESRI_TOKEN is not touched; unset it to go fully anonymous.

Example:

from georest.esri import auth

auth.logout()   # ends the session and deletes any saved credentials
georest.esri.auth.status() → dict[str, Any] | None[source]

Describe the active session, or None. Never includes the token.

Example:

from georest.esri import auth

auth.status()   # who is signed in, to which portal, until when; None if nobody
georest.esri.auth.trust_host(host: str) → None[source]

Allow the session token to be sent to host as well.

A leading dot trusts every subdomain: ".gis.myagency.gov". Use for an ArcGIS Server that trusts the portal but is not listed as federated.

Example:

from georest.esri import auth

# Also send the session token to a federated server outside the portal's domain
auth.trust_host("gis.myagency.gov")
georest.esri.auth.credentials_for(url: str) → tuple[str | None, dict[str, str]][source]

The session token and extra headers to send to url, if any.

Returns (None, {}) when no session covers the URL’s host, when the URL is plain http and the portal is not, or when the token has expired and cannot be renewed (an expired token makes even public services answer 498, so sending none is the better failure).

Example:

from georest.esri import auth

token, headers = auth.credentials_for("https://services.arcgis.com/abc/arcgis/rest/services/x/FeatureServer/0")
# token is None for hosts the session does not cover