Authentication
The example-app API uses GraphQL over HTTPS to communicate with clients. Purpose-built GraphQL clients as well as HTTP clients like fetch, cURL, or the HTTP library for your programming language of choice can be used to communicate with your Gadget app's API.
Gadget generates a JavaScript client for the example-app API which handles authentication automatically. We recommend using these clients where possible.
Authentication modes
The Gadget API supports three different authentication modes:
Mode | Description | Use cases |
---|---|---|
Browser Session authentication | Gadget stores a session token (similar to a cookie) in the user's browser, and sends it in the Authorization header | Client-side applications, browser single sign-on, user login/logout flows |
Anonymous authentication | No authentication header sent at all | Publicly accessible data, ecommerce storefronts |
API Key authentication | Copy a gsk-xxxxxx API Key from the API Keys page in the editor, and pass it in the Authorization header | Server-to-server communication, bots, scripts |
Shopify Session Token | Auth managed by Shopify's session tokens and @gadgetinc/react-shopify-app-bridge | Apps embedded in the Shopify admin |
The API client will use Browser Session authentication by default if it detects that it's running in a browser environment, and use Anonymous authentication by default if not running in a browser.
Browser session authentication
Gadget can use session tokens to authenticate individual users to the backend. Session tokens work similarly to cookies. Clients will receive a private session token representing that one client's browser, and the token will be represented in the backend by one record of the application's Session model. The client can then run Actions on this session record to change their authorization state.
If you're building a client-side application where authenticated users should have access to different data than unauthenticated users, then Browser Session Authentication is a great option. Unauthenticated users will have the Unauthenticated
role which can allow them to access some data (or none at all), and then once a user logs in, they can be given a role with different permissions.
Browser Session authentication with Gadget works quite similarly to cookie-based authentication in other systems, but Gadget doesn't actually use cookies under the hood to make cross-domain authentication work better across different browsers. Instead, Gadget uses the Authorization
header to implement a similar scheme, and the generated JavaScript client uses localStorage
to store the private session token.
Read more about implementing roles and permissions for your app in the Access Control guide.
Enabling browser authentication
If you're using the Gadget JavaScript client for example-app in a browser context, and you want to allow the user to log in and stay logged in, you can request that the Gadget client track a user's session token with the browserSession: true
authentication mode.
1import { Client } from "@gadget-client/example-app";2const client = new Client({3 authenticationMode: {4 browserSession: true,5 },6});
1import { Client } from "@gadget-client/example-app";2const client = new Client({3 authenticationMode: {4 browserSession: true,5 },6});
Storage modes for session persistence
The JS client has three modes for storing the session token which uniquely identifies the user to allow full configuration of how long a user's session might last. To configure which storage mode is used for session token persistence, pass the storageType
option like so:
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Session,6 },7 },8});
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Session,6 },7 },8});
Long-lived sessions with BrowserSessionStorageType.Durable
(Default)
In this mode, the client will persist the user's session token using window.localStorage
. This means the user's session will last generally a long time -- until the user clears local storage or the browser decides it has expired. This option is a good default for applications where users log in and can stay logged in for a long time.
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Durable,6 },7 },8});
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Durable,6 },7 },8});
Short-term sessions with BrowserSessionStorageType.Session
In this mode, the client will persist the user's session token using window.sessionStorage
. This means the user's session will last until they close the tab, which is generally a short time. Users can navigate between pages of the application, or open new tabs and preserve it, but generally sessions stored with BrowserSessionStorageType.Session
will be short lived.
This option is a good default for applications where users log in to something very sensitive, or for applications where the user's identity is ephemeral like a browser game or sales chat app.
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Session,6 },7 },8});
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Session,6 },7 },8});
Single page sessions with BrowserSessionStorageType.InMemory
In this mode, the client will not persist the user's session, so it will only last while that one page is active in the user's browser. This means the user's session will last until they navigate away from the page, close the tab, refresh, or do anything to reset the JS context of the page, which is generally a short time.
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Session,6 },7 },8});
1import { Client, BrowserSessionStorageType } from "@gadget-client/example-app";2const api = new Client({3 authenticationMode: {4 browserSession: {5 storageType: BrowserSessionStorageType.Session,6 },7 },8});
Anonymous authentication
It can be convenient to allow anyone without an API Key to access some data in your application. For example, anyone on the internet should be able to visit a blog and read the posts, or visit an ecommerce storefront and view the products, so no authentication is needed for this kind of data.
To use Anonymous authentication, make requests without sending an API key or a browser session token. Requests to the example-app app without any authentication information will be assigned the Unauthenticated
role. The Gadget application developer will need to grant the Unauthenticated
role permission to access this publicly-available data. If the Unauthenticated
Role for the application hasn't been granted any permissions, requests made without an API Key won't be able to read or write any data.
To create a client that uses no authentication, pass anonymous: true
to the authenticationMode
option when creating the client:
1import { Client } from "@gadget-client/example-app";2const client = new Client({3 authenticationMode: {4 anonymous: true,5 },6});
1import { Client } from "@gadget-client/example-app";2const client = new Client({3 authenticationMode: {4 anonymous: true,5 },6});
API key authentication
The Gadget API uses API Keys to authenticate requests. API keys are secret strings accessible through the Gadget Editor. API Keys grant the holder permission to read and write different pieces of data depending on which roles the key has been assigned.
API Keys always start with the three letters gsk
(standing for Gadget Secret Key), so they look something like gsk-a1z1z1z1z1z1z1z1z11z
. Security best practices mandate that you don't commit sensitive data like API keys to your code base, and instead use something like environment variables to pass them to code in production. GitGuardian has a great reference on how to accomplish this.
Don't send API keys to the browser as they can be read from the source code and used for malicious purposes.
API Key authentication is useful for server-to-server communication where the client code is trusted.
If you're building a server-side application that will write data to the example-app datastore, API keys are the easiest way to authenticate and limit permissions.
Sending an API key
If you're using the Gadget JavaScript client for example-app, you can pass your API Key as an option to the client when constructing it.
const client = new Gadget({authenticationMode: { apiKey: "gsk-a1z1z1z1z1z1z1z1z11z" },});
const client = new Gadget({authenticationMode: { apiKey: "gsk-a1z1z1z1z1z1z1z1z11z" },});
The client
object will automatically pass the API Key to the API for each request it makes.
If you're making requests using some other HTTP client, you must pass the API Key as the token using HTTP Bearer Auth. HTTP requests to the GraphQL endpoint at https://example-app--development.gadget.app/api/graphql
should use Bearer Token authentication in the headers to do this.
This means passing the HTTP Authorization
header with the value Bearer gsk-a1z1z1z1z1z1z1z1z11z
, replacing that example API Key with a valid API Key from the Gadget Editor.
For example, you can make an authenticated request with curl
by passing the Authorization
header.
curl -H "Authorization: Bearer gsk-a1z1z1z1z1z1z1z1z11z" -X POST https://example-app--development.gadget.app/api/graphql ...
Authentication failures
If an invalid API Key is passed to Gadget, the API will return an error in the GraphQL error format as a JSON response like so:
json1{2 "errors": [3 {4 "message": "Invalid API Key."5 }6 ],7 "data": null8}
This error is only returned if an API Key is used to authenticate. If no Authorization
header is sent with the request, which means no API Key has been passed, Gadget treats the request as an anonymous one, and permits or denies access to data using the Unauthenticated
role.
Enabling authentication with the React client
The urql
client (built on top of the Gadget React bindings) that the example-app client provides is pre-configured to connect to the right Gadget platform GraphQL endpoint and uses the same authentication mechanism that the outer Gadget client uses. If you configure the Gadget client to use a browser session, the urql
client will use the same browser session. If you configure you Gadget client to use API key authentication, the urql
client will use that same API key.
React is a client side framework and your code is likely to be visible to all the users of your application. For this reason, Gadget recommends using Session Cookie based authentication for accessing the Gadget API. You shouldn't use an API key that has write permissions for authenticating as that API key will be available to any client and ripe for abuse. You can instead use session-based authentication where users must log in to get privileges, or you can leave the client in the unauthenticated mode and grant permissions to read some backend data to the Unauthenticated
role.