Source code for georest.esri.cli.login

"""Sign in to ArcGIS Online or an ArcGIS Enterprise portal for georest.

Opens a browser for an OAuth 2.0 sign-in (enterprise SSO works) and saves
the refresh token, so later processes - an MCP server, a notebook - pick
the session up without a browser:

    georest-login --portal https://myorg.maps.arcgis.com --client-id abc123
    georest-login --status
    georest-login --logout

--portal and --client-id fall back to GEOREST_ESRI_PORTAL and
GEOREST_ESRI_CLIENT_ID. The app must list http://127.0.0.1:<port>/callback
as a redirect URL. Exit status is 0 on success, 1 otherwise.

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

    http://www.apache.org/licenses/LICENSE-2.0
"""
from __future__ import annotations

import argparse
import sys

from .. import auth

#: `--help` text, without the license header (see check_themes._HELP).
_HELP = __doc__.split("\nCopyright ")[0].rstrip()


def _print_status(info: dict | None) -> None:
    if info is None:
        print("not signed in")
        return
    for key in ("portal", "username", "source", "expires", "signin_expires", "refreshable"):
        print(f"  {key:15} {info.get(key)}")
    print(f"  {'hosts':15} {', '.join(info.get('hosts') or [])}")


[docs] def main() -> int: parser = argparse.ArgumentParser( description=_HELP, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("--portal", help="portal URL or short name (e.g. agol)") parser.add_argument("--client-id", help="the registered app's client id") parser.add_argument("--port", type=int, default=auth.DEFAULT_PORT, help=f"local redirect port (default {auth.DEFAULT_PORT})") parser.add_argument("--no-browser", action="store_true", help="print the sign-in URL instead of opening it") parser.add_argument("--no-save", action="store_true", help="do not save the session to disk") group = parser.add_mutually_exclusive_group() group.add_argument("--status", action="store_true", help="show the saved session") group.add_argument("--logout", action="store_true", help="forget the session and delete saved credentials") args = parser.parse_args() if args.logout: auth.logout() print("signed out") return 0 if args.status: info = auth.status() _print_status(info) return 0 if info else 1 try: info = auth.login(args.portal, args.client_id, port=args.port, open_browser=not args.no_browser, persist=not args.no_save) except (ValueError, RuntimeError, OSError) as exc: print(f"sign-in failed: {exc}", file=sys.stderr) return 1 print("signed in") _print_status(info) if not args.no_save: print(f" {'saved to':15} {auth.CREDENTIALS_PATH}") return 0
if __name__ == "__main__": sys.exit(main())