Let colleagues sign in to your application
A registered application is a web application your organization builds or uses that acts for a colleague, under that colleague’s own access, after they approve it. A map viewer, a report generator or a team dashboard are typical ones. The application never sees a password or an Ophiolite session: it gets a credential that works only for the person who approved it, only in the projects they chose, and only until they remove it.
Two parties, two steps
Section titled “Two parties, two steps”Signing in to a registered application involves two parties, and the person meets each once:
[colleague's browser] | 1. sign in at your identity provider (code + PKCE, the application's own client) v[identity provider] --code--> [application's server] | 2. approve the application on Ophiolite's consent page v[Ophiolite consent page] --consent handle--> [application's server] | 3. exchange: provider token + handle v [Ophiolite API] --> credential (minutes)What to notice: the identity provider says who the person is; Ophiolite’s consent says what the application may do for them. Neither alone is enough, and removing the approval in Workspace ends the application’s access at its next exchange, even while the person is still signed in at the provider.
The application’s server keeps the provider’s refresh token, the consent handle and the short-lived Ophiolite credential. The browser holds only the application’s own session cookie. A credential lasts minutes; the server exchanges again when it expires, so a removed approval or a suspended application takes effect within that time.
Register the application (an owner)
Section titled “Register the application (an owner)”- In Workspace, open your organization and choose Applications.
- Give it a name colleagues will recognise, and its return addresses: the exact
https://addresses of its callback page, one per line. - Choose what it may do: Can read, or Can read and publish.
- Leave It has its own server that can keep a secret ticked for a web application with a server. Copy the secret when it is shown; it is shown once. If it is lost, choose Make a new secret.
An owner can Suspend an application at any time; nobody can then sign in to it, and every colleague’s approval of it ends. Let members sign in again reverses the suspension; colleagues approve it again.
Some deployments also accept registration from the application itself (OAuth 2.0 dynamic
client registration, RFC 7591, at the address in
/.well-known/oauth-authorization-server). Such an application waits as Registered
itself — waiting for an owner to allow it until an owner chooses Allow this application.
Approve an application (a colleague)
Section titled “Approve an application (a colleague)”The first time you sign in to the application, Ophiolite asks Let … act for you?, says what it can do, and lets you choose All projects you can open, now and later or Only the projects I choose. Choose Allow or Don’t allow.
You see every application that acts for you under Account security → Apps you use. Remove access ends it at the application’s next exchange.
Build the server
Section titled “Build the server”The Python SDK’s ophiolite.oauth_app.OAuthApp performs both steps and the exchange, and
keeps each person’s sign-in in a private folder on the server. The smallest working server
is registered_application.py;
its sign-in part:
app = OAuthApp(args.workspace, args.application, args.client_id, args.public_origin + '/callback', store=FileStore(args.store), client_secret=os.environ.get('OPHIOLITE_CLIENT_SECRET'))sessions = {} # this server's own sessions: a random cookie value -> who signed in, and pending sign-ins
def answer(path, query, session): if path == '/sign-in': # 1. the identity provider (code + PKCE) address, session['pending'] = app.begin() return 302, address if path == '/callback' and 'code' in query: # 2. Ophiolite's consent page session['person'] = app.callback(session.pop('pending', None), query.get('code'), query.get('state')) session['consent'] = secrets.token_urlsafe(24) return 302, app.consent_url(session['consent']) if path == '/callback': # 3. the exchange, on this server app.consented(session.get('person'), session.pop('consent', None), query) return 302, '/' if path == '/projects' and session.get('person'): # a call made for the person, from this server try: with app.client(session['person'], args.project) as client: return 200, json.dumps({'wells': len(client.wells())}) except (SignInRequired, ConsentRequired): return 302, '/sign-in' return 404, json.dumps({'message': 'Not found'})Run it with the registration’s id and client id, and the secret in the environment:
export OPHIOLITE_CLIENT_SECRET=... # shown once when the application was registeredpython registered_application.py --workspace https://workspace.example \ --application <registration id> --client-id <client id> \ --public-origin https://maps.example --project <project id>The map application template (ophiolite init map-application) carries the same flow in
server/sign_in.py, with sign-out and its tests.
Same-origin rules
Section titled “Same-origin rules”- The browser never carries a token. Responses, redirects and cookies from the
application’s server carry only its own session cookie, marked
Secure,HttpOnlyandSameSite=Lax. - The exchange is server to server.
POST /api/v1/applications/exchangerefuses a request with a cookie, anOriginheader or anAuthorizationheader, so a browser cannot call it, even by mistake. - The identity provider is the deployment’s.
OAuthAppreads the provider from/.well-known/oauth-authorization-serveron your Workspace address and refuses endpoints on any other origin. - Return addresses match exactly. A return address that was not registered is refused by the identity provider and by Ophiolite’s consent page.
- The application’s own calls stay on its origin. A page of the application calls
its own server, which calls Ophiolite; the template refuses a call whose
Originis not its own.
When access ends
Section titled “When access ends”| What happened | What the application sees | What it does |
|---|---|---|
| The colleague removed it in Apps you use | ConsentRequired at the next exchange | Send them to sign in again; they approve again |
| An owner suspended the application | ConsentRequired | Nobody can sign in until an owner lets members sign in again |
| The provider session ended | SignInRequired | Send them to sign in again; their approval stays |
| The credential expired | nothing | OAuthApp exchanges again on its own |
For the endpoints, see the Application API reference.
