# A Spring Boot OAuth2 server with PKCE, user management and a client admin UI

> This repository is a self-hosted OAuth2 and OpenID Connect provider built on Spring Boot, with the authorization code and refresh token grants, a discovery endpoint, a userinfo endpoint and an admin interface for managing users and registered clients. It is aimed at teams that need an authentication centre without building one from scratch.

**jobmission/oauth2-server** — spring boot  (springboot 3+) oauth2 server sso 单点登录 认证中心 JWT,独立部署,用户管理 客户端管理

- Repository: https://github.com/jobmission/oauth2-server
- Stars: 547 · Forks: 233
- Language: Java
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jobmission-oauth2-server

## What it is, and the scope the README states

This is a Spring Boot OAuth2 server, and the repository description lists what it is meant to be: an OAuth2 server and single sign-on authentication centre, with JWT, standalone deployment, and user and client management, on Spring Boot 3 and above.

The README itself opens with the heading SpringBoot 4.0+ oauth2 server, which is worth noting because the repository description and the README disagree on the minimum version, and the newer number in the README heading is probably the accurate one.

Two endpoint families are documented from the start. The OpenID Connect discovery endpoint is the standard one every OIDC client looks for first, and it is served from the well-known path:

```
Get /.well-known/openid-configuration
```

The supported grant types are listed in the same abbreviated style:

```
authorization_code, refresh_token
```

That is the whole protocol surface, and it is a deliberately small one. The authorization code flow with PKCE and the refresh token grant cover the case of a first party web application and a long lived session, which is what an internal authentication centre usually needs. The client credentials and device code grants are not mentioned, so if you need machine to machine access with the client credentials grant, you would have to check the source rather than assume.

The project is MIT licensed, the primary language is Java, the default branch is master, the last push was on 2026-09-10, and there are no GitHub releases, so the version history is in the commit log rather than in tags. The topics name the roles it plays: oauth2-provider, oauth2-server, single-sign-on and sso.

## The authorization code flow, with PKCE required rather than optional

The README walks through the full flow in the order a client would follow, and the detail that matters is that PKCE is not optional here.

The first step is to obtain the code, and the instruction is that this requires the PKCE method. The parameters are laid out in full:

```
Get /oauth2/authorize
client_id=SampleClientId
response_type=code
redirect_uri=http://localhost:10480/login/oauth2/code/sso-login
scope=openid profile
code_challenge=6bQSbd4AdLzLhBvSPjUjthOJOt5AZcV19-pbjpeRM64
code_challenge_method=S256
```

So the flow is a standard authorization code request with a code challenge computed with the S256 method, and the scope asks for openid and profile. The plain authorization endpoint is used, which is the correct choice for a public client, and the code challenge is present on the same request.

After the user consents, the server responds and the browser is redirected to the redirect URI, carrying a code, and the backend then calls the token endpoint with it.

The second step is the token exchange, and this is a form post with a specific content type:

```
Post /oauth2/token
```

The form data carries the client id and secret, the grant type of authorization_code, the same redirect URI, the code, and the code verifier that matches the challenge:

```
client_id=SampleClientId
client_secret=tgb.258
grant_type=authorization_code
redirect_uri=http://localhost:10480/login/oauth2/code/sso-login
code_verifier=UWxD3NZtmkuitGkVdZnbkjjHgolTxOXrMpxesbGYUV3vVHYNo6yLyKGm4rzOaTUAT2phuwzXVfh-ozdulmoXrse10IHlWdj5jf7PdQR0YWFcPvHuOTwGsbTuphpMGCna
```

The response is a standard token document:

```
{
    "access_token": "a.b.c",
    "refresh_token": "d.e.f",
    "scope": "openid profile",
    "id_token": "h.i.j",
    "token_type": "Bearer",
    "expires_in": 7199
}
```

The access token, refresh token and id token are all present, the token type is Bearer and the lifetime is just under two hours, which is 7199 seconds rather than a round 7200, a detail that suggests the value is computed as a moment minus a second somewhere.

## Using the token, refreshing it, and the admin interface

Accessing a protected resource is the conventional pair of ways, either as a query parameter or in the Authorization header:

```
Get /userinfo?access_token=a.b.c
```

```
Authorization: Bearer a.b.c
```

The query parameter form is the older OAuth 2.0 style and putting a bearer token in a URL is generally discouraged, since URLs end up in logs and referrer headers. The header form is the better habit, and the server accepts both.

Refreshing is a token endpoint call with the refresh token grant:

```
Post /oauth2/token?client_id=SampleClientId&client_secret=tgb.258&grant_type=refresh_token&refresh_token=d.e.f
```

Note that this one is shown with the parameters in the query string rather than as a form body, while the authorization code exchange was shown as a form body. Both work with most servers, but the inconsistency in the documentation is worth a moment when you write the client.

The administrative side is the part that makes this a product rather than a library. After an administrator logs in, they can manage users and clients, and the README includes screenshots of the login page, the user management screen and the client management screen, served as static images from the test resources directory of the source tree.

That last point is a small but telling detail. The admin screens are static files under src/test/resources/static/imgs, which tells you the interface is a small set of server-rendered pages rather than a separate front end application, which is consistent with a tool meant to be dropped into a Spring Boot project and run.

The administration surface is the reason to choose this over wiring up an existing provider. Managing users and registered clients through a UI, rather than through SQL or an admin API, is what a team running an internal authentication centre actually spends its time on.

## What the sample values tell you about the defaults, and where to change them

The examples in the documentation are usable as they stand, and that is a source of information as well as a risk.

The client identifier is SampleClientId, the client secret is a short literal, the redirect URI points at localhost:10480 with a path of /login/oauth2/code/sso-login, and the scope is openid profile. The code challenge and code verifier in the examples are a real matched pair, so the flow can be walked end to end against a running instance without generating a fresh pair.

That is genuinely useful for evaluation, and it also means the defaults are known to anyone who has read the page. A deployment that ships the sample client secret unchanged is a deployment whose client credentials are public knowledge, so the first thing to do is register your own clients through the admin interface and delete or disable the sample one.

The redirect URI is worth the same attention, and for a different reason. The port 10480 and the path shape tell you the flow assumes the client application lives on a different origin from the authorisation server, because the code comes back through a browser redirect. That is the correct shape for a single sign-on setup, and it is also why the session cookie warning later in the documentation exists, since the two applications are adjacent in the browser's view even when they are on different ports.

The scope of openid profile is the minimum for OIDC with a user profile, and the presence of an id_token in the response confirms the provider is issuing one. If your client needs email or address claims, the scope list and the client registration both have to allow it, and neither is described in detail here, so the source is where to check what a registered client can be granted.

The token lifetime of 7199 seconds is the other default worth noting. Two hours is long for a browser session and short for a service account, and the value is a global setting rather than a per-client one as described, so if you need a different lifetime for different applications you would be adjusting it at the server level.

## Running it, and the session cookie collision you need to know about

The startup section is short, and the first form is running the packaged jar:

```
java -jar oauth2-server-x.y.z.jar
```

and the second is running it with an external configuration file that overrides the defaults:

```
java -jar oauth2-server-x.y.z.jar --spring.config.additional-location=/path/to/override.properties
```

The second form is the one to use in any real deployment, because it means the configuration is a file on disk rather than something compiled into the artefact, and the standard Spring Boot mechanism for layered configuration means the same jar can be pointed at different environments.

The warning at the end of the README is the one piece of advice in the whole document, and it is worth taking seriously. When the server and the client are on the same machine, configure a domain proxy to avoid cookies overwriting each other, or change the default session id. The suggested change is a property:

```
server.servlet.session.cookie.name=oauth2_session_id
```

The problem is a classic one in same-host deployments. The authentication server sets a session cookie to hold the login state, and the application that the user is logging in to also sets a session cookie. If both are on the same host and differ only by path or by the absence of a domain attribute, the browser sends whichever cookie is more specific or the last one written, and the two applications end up fighting over the same name. The three ways out are to serve them on different hostnames through a proxy, to change the cookie name, or to change the session id.

The default of leaving the session cookie as the standard JSESSIONID means that an unmodified deployment will hit this, and the fix is a single line of configuration. The repository top level is otherwise conventional for a Maven project: a pom.xml, a maven-version-rules.xml, a src/ directory, an .editorconfig, and the usual GitHub and git configuration. The maven-version-rules.xml is the part worth noting, since it is where a project that tags releases from a branch records the version bump rules, and it is a hint that the release numbering is handled rather than typed by hand.

## Conclusion

Adopt this server if you need a self-hosted OAuth2 and OpenID Connect provider for internal applications and want user and client management with a web interface rather than a configuration file. Do not adopt it for a public-facing consumer product without reading the implementation, since the documentation lists endpoints and sample credentials rather than a security model. Before you deploy, change the sample client secret, set the session cookie name if the server and your clients share a machine, and decide on the deployment form, since the application is run as an executable jar and can be started with an external configuration file.

## FAQ

### Which OAuth2 grant types does this Spring Boot server support?

The authorization code and refresh token grants, listed as authorization_code and refresh_token, along with an OpenID Connect discovery endpoint served from /.well-known/openid-configuration.

### Is PKCE required when getting an authorization code?

Yes, the documentation says the code is obtained using the PKCE method, with a code_challenge and code_challenge_method of S256 on the request to /oauth2/authorize, and the matching code_verifier sent with the token exchange.

### What does the token response from this OAuth2 server contain?

An access_token, a refresh_token, an id_token, the scope, a token_type of Bearer and an expires_in of 7199 seconds.

### How do I run this OAuth2 server?

With java -jar oauth2-server-x.y.z.jar, or with the same command plus --spring.config.additional-location pointing at an override.properties file, which is the form to use so configuration is external to the artefact.

### What problem does this server have when it runs on the same machine as the application it authenticates?

Session cookies overwrite each other. The documented fix is to configure a domain proxy to separate them, or to change the default session id, for example with the server.servlet.session.cookie.name property set to oauth2_session_id.

## Sources

- [Issues](https://github.com/jobmission/oauth2-server/issues)
- [jobmission/oauth2-server on GitHub](https://github.com/jobmission/oauth2-server)
- [License: MIT](https://github.com/jobmission/oauth2-server/blob/master/LICENSE)
- [README](https://github.com/jobmission/oauth2-server/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jobmission-oauth2-server
