I think security is worth it to make it right and not just to make it work. In this article we are going to discuss how to build your own auth with spring security for fun (computer games) and profit (to sell out to the gambling site eventually).
I wanted to build a site for people to show off collectables from the Counter Strike and discuss them, then I got mad, because Valve uses some deprecated auth which takes hours to understand and integrate. The first attempt was a disaster, later I got myself together, found the openid library and implemented a custom spring auth provider, which i’m going to show you in this article.
the plan
- a quick intro to Spring Security
- how Valve wants you to authenticate?
- making it work together
spring security overview
Here is an overview of spring architecture, I would encourage you to open it in a new tab and never look at it again… for our purposes we are going to need to know that:
- use Filters to intercept incoming query, build the
AuthenticationTokenand callAuthenticationManagerwith it; - use
AuthenticationManagerto iterate overAuthenticationProvidersuntil it finds the one, that authenticated a user; - use
AuthenticationProviderto implement the auth logic: call needed apis, validate responses, get users data, set roles and finish the authorization;
in case of doubt, please, refer to the original documentation or see how the default auth providers are implemented, for example UserPasswordAuthenticationFilter.
how valve wants you to authenticate?
picture is worth a thousand words, they say, so here is a sequence diagram for user authentication via steam.

putting it all together
Valve uses openid protocol which is supported in java via following dependencies
implementation("org.expressme:JOpenId:1.08")
implementation("javax.servlet:javax.servlet-api:3.1.0")
then you would have to create resources/openid-providers.properties with the following content
Steam=https://steamcommunity.com/openid/
And then in your code you might do something like this, assuming there is a button with a link to /steam/login/ on your website.
@Component
class SteamAuthService(
private val openIdManager: OpenIdManager,
) : SteamAuthProvider {
override fun getAuthUrl(): String {
val endpoint = openIdManager.lookupEndpoint("Steam")
val association = openIdManager.lookupAssociation(endpoint)
val authUrl = openIdManager.getAuthenticationUrl(endpoint, association)
return authUrl
}
}
@Controller
class LoginController(private val steamAuthProvider: SteamAuthProvider) {
@GetMapping("/login/steam")
fun loginWithSteam(): String {
val authUrl = steamAuthProvider.getAuthUrl()
return "redirect:$authUrl"
}
}
after the authentication on the Steam website user is going to return with a bunch of openid.* params in the url, the Filter should parse them, create an AuthenticationToken and pass it to the AuthenticationManager.
override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse): Authentication {
val ns = request.getParameter("openid.ns") ?: throw AuthenticationServiceException("Missing openid.ns parameter")
// ... skipping a bunch of params for brevity
val invalidateHandle = request.getParameter("openid.invalidate_handle")
val steamCallBackInfo = SteamCallBackInfo(
ns = ns,
mode = mode,
op_endpoint = opEndpoint,
claimed_id = claimedId,
identity = identity,
return_to = returnTo,
response_nonce = responseNonce,
assoc_handle = assocHandle,
invalidate_handle = invalidateHandle,
signed = signed,
sig = sig
)
val userDetails = GalleryUserDetails(User(id = steamCallBackInfo.getUserId()))
val unauthenticatedToken = SteamAuthenticationToken(userDetails).apply {
details = mapOf("steamCallBackInfo" to steamCallBackInfo, "httpRequest" to request)
}
return authenticationManager.authenticate(unauthenticatedToken)
}
testing
Every Spring article must include testing section, so you might want to visit https://csgallery.net for that:)
Steam allows one to set the key’s application domain to the localhost and it works without any issues, BUT a single steam developer account can only have one key. To test locally one would have to create a second steam account, add it on your phone, wait for some time and register a new application with localhost as the domain, it works perfectly fine and allows you to test the auth without deploying or setting up ngrok.
future
as far as i can judge an upcoming release of the spring security 7 would not change anything in the contexts of this article, but it might be useful to subsribe to the spring RSS for the updates.