Authgear 2026.10.23.0
Overview
Authgear is an open-source authentication server and identity provider. Your web and mobile applications send their users to Authgear to sign in with OpenID Connect / OAuth 2 and receive standard tokens back. Authgear supports passwords, passwordless one-time codes, passkeys, biometric login, social and enterprise single sign-on, and multi-factor authentication. It includes an admin portal for users, applications, login methods, branding and security policy, plus a GraphQL Admin API.
This SecureAnyCloud image delivers Authgear 2026-10-23.0 as a hardened, ready-to-use server on Ubuntu 24.04 on Amazon EC2. It is one version of the SecureAnyCloud identity, secrets and PKI listing on AWS Marketplace. You select it as the software version when you subscribe and launch.
What you get:
- Two projects, created on your instance.
projectis your default project. Its sign-in pages and OIDC endpoints are what your applications use, athttps://<your instance name>/.accountsis the project that the admin portal uses to sign in its own administrators. Every signing key, Admin API key, session secret and CSRF secret of both projects is generated on your instance's first boot. - nginx in front, everything else on the loopback address. nginx is the only public listener (ports 80 and 443). The Authgear server and portal run as unmodified upstream containers whose ports are published on
127.0.0.1only. - An admin portal that is not on the internet. The portal (
https://localhost:8443) and its sign-in project (https://localhost:8444) are served on the loopback address only. You reach them through an SSH tunnel. - HTTPS from the first boot. nginx serves a certificate generated on your instance (an end-entity certificate, not a certificate authority), and port 80 redirects to HTTPS. First boot sets the project's public address from the instance's EC2 public DNS name. You can change it to your own domain.
- No default login and no open sign-up. First boot creates one administrator,
admin@authgear.local, with a random password unique to your instance, before anything is reachable. Public signup is off on both projects, so nobody can create the first account or any other. You add users yourself. - Real client addresses and a fixed host name. nginx hands Authgear the real TCP peer and the configured name, never a client's own
X-Forwarded-FororHost. Sessions, audit logs and rate limits record the real address, and a request cannot change the OIDC issuer. - Databases on sockets only. PostgreSQL 16 listens only on its Unix socket with peer authentication. There is no database password, and the cluster is created on its own data volume at first boot. Valkey (the Redis-compatible session store) listens only on a Unix socket, keeps everything in memory only, and requires a per-instance password.
- Locked-down containers. The Authgear containers run as an unprivileged account with a read-only file system, no Linux capabilities and no new privileges. They are pinned by image digest, so nothing is downloaded at boot.
- Metadata access blocked. Authgear calls URLs that you configure (webhooks, social login providers). Every service and both containers are blocked from the instance metadata service, and the AMI requires IMDSv2.
- A patched base system. Ubuntu 24.04 LTS is patched at build time, with unattended security upgrades switched on (including Docker Engine).
What is on the image: Authgear 2026-10-23.0 (the upstream authgear-server and authgear-portal release images), Docker Engine, nginx, PostgreSQL 16 with pg_partman, Valkey 7.2, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet. Authgear needs outbound HTTPS for social login providers, webhooks and any mail or SMS providers you add.
- A security group that allows TCP 22 (SSH, also used for the admin portal tunnel) from your own networks only. It must also allow TCP 443 (sign-in pages and OIDC endpoints) and TCP 80 (redirects to HTTPS) from your users. Do not open 8443, 8444, 3000-3003, 5432 or 6379. They listen on the loopback address only.
- For production, a DNS name that you control (for example
auth.example.com) and a certificate for it from a certificate authority.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Authgear 2026-10-23.0 on Ubuntu 24.04 as the software version and select your Region. Choose Launch through EC2, then select m5.large, your key pair and a security group as described above.
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --image-id <ami-id-from-the-listing> --instance-type m5.large \
--key-name <your-key-pair> --security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=authgear}]'The image has two disks: an 8 GiB system disk and a 20 GiB gp3 data volume mounted at /var/lib/postgresql. The data volume holds the PostgreSQL cluster (users, credentials, OAuth grants, the audit log and the project configuration). Increase its size at launch if you expect many users.
First boot takes about half a minute after the instance is running. To use your own name from the start, write it to /etc/sac/authgear-domain with user data (for example a cloud-init write_files entry). Otherwise, first boot uses the instance's public DNS name, or its public IP address if it has no DNS name.
Connect to your instance
Operating system SSH user
Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
systemctl is-active authgear-firstboot docker postgresql@16-main valkey-server authgear-server authgear-portal nginxactive active active active active active activeOnly SSH, HTTP and HTTPS are public. The Authgear server (main 3000, Admin API 3002), the portal (3003) and the portal's tunnel listeners (8443, 8444) are on the loopback address. PostgreSQL and Valkey have no TCP listener at all:
sudo ss -tlnp | grep -E ':(22|80|443|3000|3002|3003|5432|6379|8443|8444) ' | awk '{print $4}' | sort -u0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 127.0.0.1:3000 127.0.0.1:3002 127.0.0.1:3003 127.0.0.1:8443 127.0.0.1:8444 [::]:22 [::]:443 [::]:80curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/healthz200Get your sign-in and the certificate fingerprint
sudo cat /root/authgear-credentials.txtThe file is readable by root only (mode 0600). It lists the administrator (the password is shown here as <redacted>), the project's public address, its OIDC discovery URL and the SHA-256 fingerprint of the instance's certificate, followed by a short how-to:
# Authgear 2026-10-23.0 - written on first boot by authgear-firstboot.service. Unique to this instance. AUTHGEAR_ADMIN_EMAIL=admin@authgear.local AUTHGEAR_ADMIN_PASSWORD=<redacted> AUTHGEAR_PORTAL_VIA_SSH_TUNNEL=https://localhost:8443/ AUTHGEAR_PROJECT_ID=project AUTHGEAR_PROJECT_URL=https://ec2-52-23-159-80.compute-1.amazonaws.com/ AUTHGEAR_OIDC_DISCOVERY=https://ec2-52-23-159-80.compute-1.amazonaws.com/.well-known/openid-configuration AUTHGEAR_TLS_SHA256=D7:B5:37:F3:18:96:79:05:E1:66:2D:0F:5F:F7:53:CF:83:45:75:0D:44:CC:23:57:06:26:62:9A:5D:55:65:7FThe login message and the following commands also show the name, every address the instance answers to and the certificate fingerprint:
authgear-set-hostname --showname: ec2-52-23-159-80.compute-1.amazonaws.com project URL: https://ec2-52-23-159-80.compute-1.amazonaws.com/ OIDC: https://ec2-52-23-159-80.compute-1.amazonaws.com/.well-known/openid-configuration portal: https://localhost:8443/ (SSH tunnel) also served: ec2-52-23-159-80.compute-1.amazonaws.com 52.23.159.80 172.31.80.45 certificate: SHA-256 D7:B5:37:F3:18:96:79:05:E1:66:2D:0F:5F:F7:53:CF:83:45:75:0D:44:CC:23:57:06:26:62:9A:5D:55:65:7FThe certificate is self-signed and unique to your instance, so your browser shows a warning. Open the certificate details in the browser and check that its SHA-256 fingerprint matches AUTHGEAR_TLS_SHA256. Continue only if it matches. Do not add the certificate to your operating system's trust store. For production, use your own domain and a certificate from a certificate authority (Step 10).
Open the admin portal through an SSH tunnel
From your computer, forward the two loopback listeners:
ssh -i <your-key.pem> -L 8443:127.0.0.1:8443 -L 8444:127.0.0.1:8444 ubuntu@<public-ip>Then browse to https://localhost:8443/. The portal sends you to its own sign-in project at https://localhost:8444. There is no sign-up link. The only portal account is the one that first boot created.
Sign in with AUTHGEAR_ADMIN_EMAIL and AUTHGEAR_ADMIN_PASSWORD. The portal lists the two projects: accounts (the portal's own sign-in project, at https://localhost:8444) and project, your default project at the instance's name.
Open the project to manage your default project.
Leave the accounts project as it is. It controls who can administer the portal.
Register your application
Under Applications > Client Applications, add an application. The Start Integration button on Getting Started opens the same form. Enter a name and pick your application's type: single-page app, server-side web app, mobile app, or Other OIDC/SAML for any standard OpenID Connect client. Then set the application's redirect URIs to the addresses in your application that Authgear returns users to.
Your application reads the project's discovery document, which names the instance's HTTPS address as the issuer:
D=$(cat /etc/sac/authgear-domain)
curl -s --cacert /etc/ssl/authgear/authgear.crt "https://$D/.well-known/openid-configuration" | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(k+":", d[k]) for k in ("issuer","authorization_endpoint","token_endpoint","userinfo_endpoint","jwks_uri")]'issuer: https://ec2-52-23-159-80.compute-1.amazonaws.com authorization_endpoint: https://ec2-52-23-159-80.compute-1.amazonaws.com/oauth2/authorize token_endpoint: https://ec2-52-23-159-80.compute-1.amazonaws.com/oauth2/token userinfo_endpoint: https://ec2-52-23-159-80.compute-1.amazonaws.com/oauth2/userinfo jwks_uri: https://ec2-52-23-159-80.compute-1.amazonaws.com/oauth2/jwksMost OIDC libraries need only the issuer, the client ID from the portal and your redirect URI. Switch to your own domain (Step 10) before you register production redirect URIs, because the issuer changes with the name.
Add users
Public signup is off, so you create the accounts. In the portal, use User Management > Users > Create User, or script it with the Admin API. The image's authgear-admin-api helper signs each call with the project's Admin API key. The query and the variables are files, so a password never appears on a command line. The following commands create jane@example.com with a random password, which the helper keeps in a root-only file for you to hand over:
T=$(sudo mktemp -d /run/ag-guide.XXXXXX) printf '%s\n' 'mutation ($email: String!, $password: String!) { createUser(input: { definition: { loginID: { key: "email", value: $email } }, password: $password }) { user { id standardAttributes } } }' | sudo tee $T/q.graphql >/dev/null
sudo python3 -I -c 'import json,secrets,os; pw="Jn-"+secrets.token_urlsafe(18); os.umask(0o077); open("/root/jane-password.txt","w").write(pw+"\n"); print(json.dumps({"email":"jane@example.com","password":pw}))' | sudo tee $T/v.json >/dev/null
sudo authgear-admin-api project $T/q.graphql $T/v.json | python3 -c 'import json,sys; u=json.load(sys.stdin)["data"]["createUser"]["user"]; print("created", u["standardAttributes"]["email"])'
sudo find $T -type f -exec shred -u {} +;
sudo rm -rf $Tcreated jane@example.comList the project's users in the same way:
T=$(sudo mktemp -d /run/ag-guide.XXXXXX) printf '%s\n' 'query { users(first: 10) { totalCount edges { node { standardAttributes } } } }' | sudo tee $T/q.graphql >/dev/null
sudo authgear-admin-api project $T/q.graphql | python3 -c 'import json,sys; d=json.load(sys.stdin)["data"]["users"]; print("users:", d["totalCount"]); [print(" ", e["node"]["standardAttributes"].get("email")) for e in d["edges"]]'
sudo rm -rf $Tusers: 1 jane@example.comChoose how users sign in under Authentication > Login Methods. The options are email, phone, username, passkeys, social and enterprise providers, and 2FA.
No mail server or SMS gateway is configured. Email verification, one-time codes and password resets by email or SMS do not work until you add your own provider under Advanced > Custom Email Provider and Custom SMS Gateway.
What your users see
Your users sign in at the project's public address. Your application normally sends them there through the OIDC authorize endpoint.
An address that has no account is refused. The visitor is never taken to a page where they could set a password and create an account.
Check the server from the command line
One command checks the whole chain on the instance. It covers the discovery document and signing keys over HTTPS, a signed Admin API call (and an unsigned one refused), the portal and its sign-in project on the tunnel listeners, and public signup refused on both projects. It also checks that the administrator's password is accepted and a wrong one refused, Valkey's password, and PostgreSQL's socket-only, passwordless setup:
sudo /usr/local/sbin/authgear-cred-roundtrip.shAUTHGEAR_ROUNDTRIP_OK discovery=200 jwks=RSA adminapi=signed-ok,unsigned-refused portal=200 accounts=200 signup=refused(project,accounts) admin-login=finished wrong-password=InvalidCredentials auth=PONG anonymous=NOAUTH wrong=WRONGPASS config=NOPERM tcp=none pg=socket-only,no-passwordsUse your own domain and certificate
Point a DNS name at the instance. An Elastic IP is recommended. Then run:
sudo authgear-set-hostname auth.example.comThe default project's public address becomes https://auth.example.com, which is also its OIDC issuer. nginx answers to that name, the instance's own addresses redirect to it, and the self-signed certificate is re-issued for it. Update your applications' issuer and redirect URIs.
To serve a certificate from a certificate authority, replace /etc/ssl/authgear/authgear.crt (full chain) and /etc/ssl/authgear/authgear.key (mode 0600, root), then run sudo systemctl reload nginx. authgear-set-hostname never overwrites a certificate that is not its own. Keep localhost in the certificate's names, or keep using the self-signed certificate for the 8443/8444 tunnel listeners.
Public signup
Public signup is off on both projects. The project's configuration lives in PostgreSQL. authgear-config exports it to a root-only directory on /run (memory) and imports it back. Check the setting:
T=$(sudo mktemp -d /run/ag-cfg.XXXXXX)
sudo authgear-config export project $T 2>/dev/null
sudo grep -A1 '^authentication:' $T/authgear.yaml
sudo find $T -type f -exec shred -u {} +;
sudo rm -rf $Tauthentication: public_signup_disabled: trueTo let anyone who can reach the project create an account, export the configuration as above and change public_signup_disabled to false in $T/authgear.yaml. Run sudo authgear-config import $T, then shred the directory. The server picks up the change within a few seconds.
Enable signup only for the project project. Never enable it for accounts, because anyone could then sign up to your admin portal. Before you turn signup on, consider adding bot protection and a mail provider for email verification.
Check the security posture
The certificate is an end-entity certificate for the instance's name and addresses (plus localhost for the tunnel). It cannot sign other certificates:
openssl x509 -in /etc/ssl/authgear/authgear.crt -noout -subject -ext basicConstraints,keyUsage,extendedKeyUsage,subjectAltNamesubject=CN = ec2-52-23-159-80.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-52-23-159-80.compute-1.amazonaws.com, IP Address:52.23.159.80, IP Address:172.31.80.45, DNS:localhost, IP Address:127.0.0.1 X509v3 Basic Constraints: critical CA:FALSE X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server AuthenticationTLS 1.0 and 1.1 are refused, and a request for a host name the instance does not serve gets no answer:
echo | openssl s_client -tls1_1 -cipher 'DEFAULT@SECLEVEL=0' -connect 127.0.0.1:443 2>&1 | grep -o 'alert protocol version' | head -1 code=$(curl -sk -o /dev/null -w '%{http_code}' -H 'Host: evil.example' "https://$(hostname -I | awk '{print $1}')/login" || true);
echo "$code"; [ "$code" = 000 ]alert protocol version 000nginx hands Authgear the configured name and the real client address, never values from the request:
grep -E 'proxy_set_header +(Host|X-Forwarded-For|X-Real-IP|X-Forwarded-Host) ' /etc/nginx/sac/authgear-proxy-project.confproxy_set_header Host ec2-52-23-159-80.compute-1.amazonaws.com; proxy_set_header X-Forwarded-Host ec2-52-23-159-80.compute-1.amazonaws.com; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Real-IP $remote_addr;The containers publish their ports on the loopback address only:
sudo docker ps --format '{{.Names}} {{.Ports}}'authgear-server 127.0.0.1:3000->3000/tcp, 3001/tcp, 127.0.0.1:3002->3002/tcp, 13000-13002/tcp authgear-portal 127.0.0.1:3003->3003/tcp, 13003/tcpMetadata access is blocked for the host services (a systemd rule) and for the containers (a firewall rule in Docker's DOCKER-USER chain). A connection from inside each container never gets through:
systemctl show -p IPAddressDeny nginx authgear-firstboot valkey-server | sort -u | grep .
sudo iptables -S DOCKER-USER | grep DROP for c in authgear-server authgear-portal; do
sudo docker exec $c timeout 8 bash -c 'exec 3<>/dev/tcp/169.254.169.254/80' 2>/dev/null && echo "$c: metadata REACHED" || echo "$c: metadata blocked"; doneIPAddressDeny=168.63.129.16/32 169.254.169.254/32 ::ffff:169.254.169.254/128 fd00:ec2::254/128 -A DOCKER-USER -d 168.63.129.16/32 -j DROP -A DOCKER-USER -d 169.254.169.254/32 -j DROP authgear-server: metadata blocked authgear-portal: metadata blockedAuthgear's own SSRF protection also refuses webhooks to private and link-local addresses. Build validation of the image showed that, even with that protection lifted, the network drops a webhook to the metadata service while a webhook to an ordinary receiver is delivered.
PostgreSQL has no TCP listener and no role has a password. Valkey refuses anonymous and wrong-password access and every administrative command:
sudo -u postgres psql -X -qAt -c "SHOW listen_addresses;" -c "SELECT count(*) FROM pg_authid WHERE rolpassword IS NOT NULL;" | sed 's/^$/(none)/'
sudo authgear-valkey-password check(none) 0 VALKEY_PASSWORD_OK auth=PONG anonymous=NOAUTH wrong=WRONGPASS config=NOPERM tcp=noneCore dumps are off for every service and SSH accepts keys only:
systemctl show -p LimitCORE --value nginx authgear-server authgear-portal valkey-server docker | tr '\n' ' '; echo
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|x11forwarding) '0 0 0 0 0 permitrootlogin no passwordauthentication no x11forwarding noPasswords and secrets
- The administrator password. The portal administrator is a user of the
accountsproject. Set a new password with the Admin API'sresetPasswordmutation on that project, with the query and the variables in files exactly as in Step 7:
graphql mutation ($id: ID!, $pw: String!) { resetPassword(input: { userID: $id, password: $pw }) { user { id } } }
Run it as sudo authgear-admin-api accounts <query-file> <variables-file>. Find the user's id with a users(searchKeyword: "admin@authgear.local") query. Then update /root/authgear-credentials.txt yourself, or delete it.
The Valkey password (Authgear's session store) can be rotated at any time. Valkey keeps sessions in memory only, so every signed-in user is signed out:
bash sudo authgear-valkey-password rotate
- Signing keys. The projects' OIDC signing keys, Admin API keys and session secrets were generated on this instance and are stored in PostgreSQL. None of them exists on any other instance.
Your data and backups
Authgear stores everything in the PostgreSQL database authgear on the data volume: users, credentials, sessions of record, OAuth grants, the audit log and both projects' configuration and keys. Back it up with pg_dump over the local socket. No password is needed, and none exists:
sudo -u postgres pg_dump -Fc authgear | sudo tee /root/authgear-$(date +%F).dump >/dev/null
sudo chmod 600 /root/authgear-$(date +%F).dump;
sudo ls -l /root/authgear-$(date +%F).dump | awk '{print $1, $3}'-rw------- rootTreat the dump like a password store. It holds password hashes and the projects' private signing keys.
Valkey holds only short-lived data (sessions, one-time codes, rate-limit counters) in memory and needs no backup. The audit log is partitioned by month. A daily authgear-audit-maintain timer keeps 180 days of audit log entries (90 days of audit metrics) and removes older partitions.
Licences and source
Authgear is licensed under the Apache License 2.0. The image carries the licence and a source record that names the release tag, the image digests and the sha256 of each binary:
ls /usr/share/doc/sac-authgear/
sudo docker image
ls --digests --format '{{.Repository}} {{.Digest}}'LICENSE-authgear-Apache-2.0.txt SOURCE.txt quay.io/theauthgear/authgear-server sha256:03266e1425c7b56c9d455e1c20df5473ee04d1bfc7bc47f5873b05d9ae131399 quay.io/theauthgear/authgear-portal sha256:141ef347c83bebf8335b834eb7336cab3962764de1448c5841d88e403be4b667- Source: github.com/authgear/authgear-server at tag 2026-10-23.0 (commit dd7188dc6334). The server and the admin portal are built from that one repository. SecureAnyCloud ships the unmodified upstream release images for that tag (quay.io/theauthgear/authgear-server and authgear-portal), pinned by their linux/amd64 manifest digests. Release 2026-10-23.0 is the 2026-10-22.0 release rebuilt by upstream on Go 1.26.10.
- PostgreSQL, pg_partman, Valkey and nginx are Ubuntu 24.04 packages. Docker Engine comes from Docker's own repository.
Security notes
- Keep port 22 open to your own networks only. Do not open the loopback ports in a security group. Reach the portal through SSH.
- Keep public signup off unless you turn it on for
project(Step 11). Never turn it on foraccounts. - In production, use your own domain and a certificate from a certificate authority, and register your applications' redirect URIs against that name.
- No mail or SMS provider is configured. Password resets and verification messages need one.
- Known residual advisories (reviewed for this release, none reachable from the network):
github.com/go-jose/go-jose/v44.1.4 in the server image (CVE-2026-116587, fixed in 4.1.6 after this release). It is linked only through the Google Cloud Storage client's SPIFFE/xDS code, which parses operator-supplied bundles, never a remote party's. Authgear's own token and JWK handling uses a different library.- Valkey 7.2.13 from Ubuntu universe (USN-8771-1: CVE-2026-56684 and CVE-2026-63639, fixed only in Ubuntu Pro ESM). The first needs Valkey's TLS listener, which is off. The second needs the
RESTOREcommand, which is refused for Authgear's account. Valkey listens on a Unix socket only. follow-redirects1.16.0 (HIGH) andip-address10.7.1 (MEDIUM) appear in the web front ends' package lists. The first is the Node-only HTTP adapter of a library and is not in the browser bundles. The second is used only in the admin portal's IP-blocklist form, in the administrator's own browser.libbrotli1andzlib1ginside the server image (two MEDIUM Ubuntu advisories) are used only by the image-processing service, which this appliance does not run.- govulncheck reports no reachable advisory in either Go binary.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and live chat at sales@secureanycloud.com. Authgear is a trademark of its respective owner. SecureAnyCloud is not affiliated with or endorsed by it.
Deploy on AWS
Launch Authgear 2026-10-23.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com