SSO vs OAuth: Are They the Same Thing?. SSO vs OAuth: no, they are not the same. OAuth 2.0 lets an app call an API with limited permissions, SSO is one login for many apps. See both tokens in Keycloak.
No, OAuth and SSO are not the same thing. OAuth 2.0 is an authorization framework that lets an application call an API with limited permissions, while single sign-on (SSO) is the result of many applications trusting one identity provider for login, usually over OpenID Connect (which is built on OAuth 2.0) or SAML.
The two get mixed up because they look alike from the browser: both start with a redirect to a central server and both end with the application holding a token. The difference is what that token is for and who reads it. This post compares the two in one table, explains the OpenID Connect bridge that makes “OAuth SSO” a real phrase, and then shows the difference in a Keycloak realm, down to the two tokens one login returns.
Key takeaways
- OAuth 2.0, defined in RFC 6749, answers “what may this application access?” and issues an access token that an API reads.
- SSO answers “who is this user?” for many applications at once. The Keycloak server administration guide says applications “are given an identity token or assertion that is cryptographically signed” instead of ever seeing the password.
- OpenID Connect Core 1.0 calls itself “a simple identity layer on top of the OAuth 2.0 protocol”, and that layer is where the two meet.
- You can have OAuth with no SSO (the client credentials grant, with no user at all) and SSO with no OAuth (SAML).
SSO vs OAuth at a glance
This table covers the points that cause most of the confusion. The short version is that an OAuth access token is addressed to an API and says what the caller may do, while the token or assertion behind SSO is addressed to the application and says who signed in and when.
| OAuth 2.0 | SSO | |
|---|---|---|
| What it is | An authorization framework (RFC 6749) | A login arrangement: many applications trust one identity provider |
| Question it answers | What may this application access? | Who is this user, and when did they sign in? |
| Typical protocols | OAuth 2.0 grants such as authorization code and client credentials | OpenID Connect or SAML 2.0 |
| Token | An access token, limited to specific scopes | An ID token (OIDC) or a signed assertion (SAML) |
| Who reads it | The resource server, meaning the API | The application the user is signing in to |
| Works without a user | Yes, through the client credentials grant | No, there is always a person signing in |
| Typical example | A reporting tool reading files from your cloud drive | Opening your CRM and your wiki after one company login |
What is OAuth 2.0?
OAuth 2.0 is a framework for delegated authorization. RFC 6749 describes it as a way for a third-party application to obtain limited access to an HTTP service, either on behalf of a user or on its own behalf. The word that matters is “limited”: the user grants an application permission to do specific things, and the application receives a token standing for that permission rather than the user’s password.
Four roles appear in every OAuth conversation: the resource owner who can grant access, the client application asking for it, the authorization server that authenticates the user and issues tokens (the role Keycloak plays), and the resource server, the API that checks them. The token itself is an access token, which section 1.4 of RFC 6749 says represents “specific scopes and durations of access”. Scopes are the named permissions a token carries, such as read-only access to a calendar, and we cover them in detail in our post on OAuth scopes.
When a user is present, the usual flow is the authorization code grant from section 4.1 of RFC 6749: the application redirects the browser to the authorization server, the user signs in and approves, the server redirects back with a one-time code, and the application exchanges that code for an access token in a server-to-server call before calling the API.

The application never handles the password, and the token says what the application may do, not reliably who the user is. That second point is why plain OAuth is not an authentication protocol or SSO on its own. Our visual guide to OAuth 2.0 walks through the flow step by step.
What is single sign-on?
Single sign-on means a user authenticates once with a central identity provider and then reaches several applications without entering credentials again; it is what our single sign-on feature delivers over OpenID Connect and SAML. The OASIS SAML technical overview calls the identity provider an asserting party, “a system entity that makes SAML assertions”, and the application a relying party, one “that uses assertions it has received” on the strength of a trust relationship between the two. If the identity provider role is new to you, our explainer on what an identity provider is covers it.
The flow is similar in shape to OAuth, which is part of the confusion. The user opens application A, which redirects the browser to the identity provider. The user signs in, the identity provider keeps that login in a session and sends application A a signed statement about the user (an ID token in OpenID Connect, an assertion in SAML). When the user later opens application B, it also redirects to the identity provider, but because the session already exists the identity provider returns a statement for B without asking for a password again. That silent second login is the single sign-on.
The first login creates a session at the identity provider. The second application redirects there too, but the session answers for the user, so no password prompt appears.

The diagram shows the SAML version. SSO is the outcome, and SAML and OpenID Connect are two protocols that produce it, which is a distinction we unpack in the difference between SSO and SAML and in SAML vs OIDC: when to use which.
Where do SSO and OAuth meet?
The bridge between the two is OpenID Connect (OIDC). The OpenID Connect Core 1.0 specification defines it as “a simple identity layer on top of the OAuth 2.0 protocol”: it reuses the OAuth authorization code flow, but when the request includes the openid scope the authorization server also returns an ID token, a signed JSON Web Token (JWT) describing the user who just logged in. The Keycloak server administration guide draws the same line, describing OAuth 2.0 as a framework for building authorization protocols and OIDC as a full authentication and authorization protocol.
So when someone says “OAuth SSO”, read it as SSO built with OpenID Connect. One login hands the application an ID token to sign the user in and an access token to call APIs, and the identity provider’s session gives the user SSO across every other application that trusts it. Our OpenID Connect explainer for developers goes deeper on the protocol itself.
Identity management as a service, on open source
Skycloak does what Auth0 and Okta do, SSO, MFA, SCIM, audit logs and enterprise federation, on an open source core. Unlimited users and applications on every plan, no charge per monthly active user, and you can export and self-host whenever you want.
Two cases that prove they are different
If OAuth and SSO were the same thing, you could not have one without the other, and these two cases show that you can.
OAuth with no SSO and no user. The client credentials grant in section 4.4 of RFC 6749 lets a client get an access token with only its own credentials. Nobody signs in, so there is no login to share, yet it is still OAuth 2.0 because an API checks the resulting token. In Keycloak you turn on Client authentication and Service account roles for the client; the server administration guide says the second setting “enables support of Client Credentials Grant for this client”.
SSO with no OAuth at all. SAML 2.0 is an OASIS standard unrelated to the IETF’s OAuth. Its technical overview says that “multi-domain web single sign-on is arguably the most important use case for which SAML is applied”. The Keycloak server administration guide describes SAML 2.0 as “an authentication protocol that exchanges XML documents between authentication servers and applications”, so what the application receives is a signed XML assertion rather than an OAuth access token with scopes, and the user still signs in once and reaches every connected application.
See both tokens from one Keycloak login
The clearest way to understand the split is to look at the two tokens a single OpenID Connect login returns. These steps follow the server administration guide for the Keycloak 26 release line, which is where each quoted setting description comes from.
- Create or pick a realm. A realm is Keycloak’s isolated space for a set of users, clients and settings; use one of your own rather than
master. - Create the client. Following the guide’s steps for creating an OpenID Connect client, go to Clients, click Create client, leave Client type on OpenID Connect, and enter a Client ID such as
my-app. - Decide confidential or public. On the capability step, Client authentication on means the client gets a secret. The server administration guide says the setting “should be used for server-side applications” and that such clients “are referred as confidential clients”. A browser or mobile app cannot keep a secret, so leave it off and require PKCE instead; the same guide says PKCE “prevents the attacker from receiving the tokens” for a stolen code. Our PKCE client guide shows that setup.
- Keep Standard flow on. The same guide says that with Standard flow enabled “this client can use the OIDC Authorization Code Flow”. Turn on Service account roles only if the client also needs the client credentials grant.
- Set the redirect URIs. Under Valid redirect URIs, add the exact callback URL, for example
https://app.example.com/callback, and avoid wildcards in production.
A second client in the same realm is where the SSO half shows up. Once the user has signed in through my-app, a login started by another client in that realm finds the existing session; the guide notes that a user “already authenticated due to an active SSO session” usually “does not need to actively re-authenticate” (re-authentication section), so the second application gets its tokens without a password prompt.
Now start a login by sending the browser to the realm’s authorization endpoint, which the guide’s endpoint list gives as /realms/{realm-name}/protocol/openid-connect/auth, without the /auth/ prefix that older tutorials show. Line breaks are added for reading:
https://keycloak.example.com/realms/my-realm/protocol/openid-connect/auth
?client_id=my-app
&response_type=code
&scope=openid%20profile%20email
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&state=af0ifjsldkj
&nonce=n-0S6_WzA2Mj
The openid scope is what asks for an ID token, and nonce is a random value your application later checks inside it. After the user signs in, exchange the returned code at the token endpoint. A public client sends no secret and adds its PKCE code_verifier instead.
curl -X POST "https://keycloak.example.com/realms/my-realm/protocol/openid-connect/token" \
-d grant_type=authorization_code \
-d code="<authorization-code>" \
-d redirect_uri="https://app.example.com/callback" \
-d client_id=my-app \
-d client_secret="<client-secret>"
The JSON response carries an access_token and an id_token in one body, since the OpenID Connect token response adds the ID token beside the OAuth access token, and in a default Keycloak client a refresh_token as well. The pair below is an illustration written for this post rather than a capture from a live realm, with the signature left off and IDs shortened. Your own claims depend on your realm’s client scopes and mappers, so treat it as the shape, not an exact copy.
The ID token is the SSO half. Your application reads it to learn who signed in:
{
"iss": "https://keycloak.example.com/realms/my-realm",
"sub": "4f1c2b7e-9a3d-4c55-8e21-6b0d9f3a7c10",
"typ": "ID",
"aud": "my-app",
"azp": "my-app",
"iat": 1792227600,
"exp": 1792227900,
"auth_time": 1792227590,
"nonce": "n-0S6_WzA2Mj",
"sid": "b7e3a1d2-...",
"at_hash": "77QmUPtjPfzWtF2AnpK9RQ",
"email_verified": true,
"preferred_username": "jane",
"email": "jane@example.com"
}
The access token is the OAuth half. Your API reads it to decide what the caller may do:
{
"iss": "https://keycloak.example.com/realms/my-realm",
"sub": "4f1c2b7e-9a3d-4c55-8e21-6b0d9f3a7c10",
"typ": "Bearer",
"aud": "account",
"azp": "my-app",
"iat": 1792227600,
"exp": 1792227900,
"auth_time": 1792227590,
"sid": "b7e3a1d2-...",
"scope": "openid profile email",
"realm_access": {
"roles": ["default-roles-my-realm", "offline_access", "uma_authorization"]
},
"resource_access": {
"account": {
"roles": ["manage-account", "manage-account-links", "view-profile"]
}
},
"preferred_username": "jane",
"email": "jane@example.com"
}
The claims that differ tell you who each token is for:
| Claim | ID token (SSO) | Access token (OAuth) | Why it differs |
|---|---|---|---|
aud |
my-app, the client that signed the user in |
account here, or the API you configure |
The OIDC ID token definition requires the relying party’s client ID; the access token is addressed to a resource server. |
scope |
Absent | openid profile email |
Scopes are for the API that enforces them. |
nonce |
The value your app sent | Absent | The OIDC ID token definition says it is there “to mitigate replay attacks”; since the Keycloak 25 upgrade notes it is added only to the ID token. |
auth_time |
Present | Present | The OIDC ID token definition lists it as the “time when the End-User authentication occurred”; Keycloak’s default basic client scope carries a mapper for it, which is why the illustrated access token has it too. |
| Roles | Absent | realm_access, resource_access |
Role claims belong with the token the API checks. |
The aud of account comes from Keycloak’s Audience Resolve mapper, which “checks for clients that have at least one client role available for the current token” and adds each as an audience; the illustrated token carries roles on the built-in account client. For a longer comparison, see our post on ID tokens vs access tokens.
To check your own tokens, paste id_token and access_token from the response into our JWT Token Analyzer. The one with nonce and an aud equal to your client ID is the ID token your application validates and uses to start a session. The one with scope and role claims is the access token your API checks, confirming its own name in aud rather than treating the token as proof that someone just logged in.
Which one do you need?
You rarely choose between them, because they solve different problems. As a starting point rather than a rule from any standard: an app that signs users in and calls its own API needs both, through OpenID Connect with the authorization code flow and PKCE. Internal apps where employees should sign in once need SSO, over OpenID Connect or SAML for apps that only support it. A third-party integration reading a user’s data from your API needs OAuth with narrow scopes, and a background job or service-to-service call needs OAuth through client credentials and no SSO at all.
Frequently asked questions
Is OAuth an SSO protocol?
No. OAuth 2.0 defines how an application gets permission to call an API, and its access tokens are not designed to tell the application who the user is. For SSO on top of OAuth you use OpenID Connect, which adds the ID token.
Is OIDC the same as OAuth?
OpenID Connect is built on OAuth 2.0 and adds authentication to it. Every OIDC login is an OAuth 2.0 authorization request with the openid scope, and the response adds an ID token describing the user, so OIDC uses OAuth while plain OAuth is not OIDC.
What to do next
OAuth decides what an application may access, SSO lets a user sign in once across applications, and OpenID Connect is where one login produces both an ID token for the application and an access token for the API. Sign in to your own app, paste each token into the JWT Token Analyzer, and compare aud, scope, nonce and auth_time to see which is which. If you would rather have the servers, scaling and upgrades handled for you without running Keycloak yourself, our Managed Keycloak page explains what that looks like.



