Abstract
The landscape of authentication has changed a lot over the past couple of decades; traditionally companies would setup user accounts on some sort of a local authentication service such as Active Directory and manage their user accounts and permissions there, however, today’s world is much more interconnected and security conscious than it used to be. Because of this many companies have moved on to Identification as a Service (IDaaS) products, such as Microsoft Entra and Google Cloud Identity. These products are built on top of open protocols such as OAuth and OpenID, and in the world of business, these products are vital in providing the three A’s of security, Authentication, Authorization, and Accountability to help keep companies secure from threat actors. What about the humble home-laber though? Yes, both Entra and Cloud Identity have a free tier, but those are proprietary online services that cannot be self-hosted. Thankfully there are tools that can be self-hosted so that you can become your own OAuth and IDaaS provider, the one I am going to cover is Keycloak, however there are other products that provide roughly the same capabilities.
It is worth pointing out that there are a few topics in this project that are fairly big, as well as the project itself. Because of that, I am going to break this up into multiple parts to make writing a bit easier (potentially reading easier too). This part is going to go over the basics of OAuth and friends, what they do, how they are related, and the most basic setup of Keycloak to use it with another application.
What are OpenID and OAuth?
While understanding these concepts aren’t strictly required for setting up and running an OAuth system, I do believe that not having at least a basic understanding of those concepts is a disservice when doing so. Even beyond this project, these concepts create the foundation of modern identity management systems that are widely used in the industry today; learning and understanding how they work will be key for progressing within the field of IT, especially within security and operations roles.
Let’s start by going over OAuth 2.0, which counter intuitively (at least to me) is an authorization protocol, not an authentication protocol. Specifically, let’s start by going over the problem that OAuth is attempting to solve:
In the traditional client-server authentication model, the client requests an access-restricted resource (protected resrouce) on the server by authenticating with the server using the resource owner’s credentials. In order to provide third-party applications access to restricted resources, the resource owner shares its credentials with the third party. This creates several problems and limitations:
- Third-party applications are required to store the resource owner’s credentials for future use, typically a password in clear-text
- Servers are required to support password authentication, despite the security weaknesses inherent in passwords.
- Third-party applications gain overly broad access to the resource owner’s protected resources, leaving resource owners without any ability to restrict duration or access to a limited subset of resources.
- Resource owners cannot revoke access to an individual third party without revoking access to all third parties, and must do so by changing the third party’s password.
- Compromise of any third-party application results in compromise of the end-user’s password and all of the data protect by that password.
To summarize, traditional access and authentication (usually passwords) are inseparable which causes problems when needing to revoke access to a resource or when dealing with a compromised system. OAuth’s answer to this problem is adding an authorization layer after the user authenticates, but before gaining access to server resources. Rather than authentication inherently granting access, the authentication will grant a token that will be accepted in place of authentication credentials. Think about this like buying tickets to a fair. You buy tickets at the gate, then use the tickets to gain access to resources (rides, games, etc.) within the fair. The most obvious benefit is being able to separate the authentication from the access to resources and removing the need for usernames and passwords to be stored on the servers, increasing security.
However, as stated above, OAuth 2.0 does not actually handle authorization. So, what do you add to it to provide proper identity management? This is where OpenID Connect comes in; OpenID Connect (usually referred to as OpenID) was created specifically to be used with OAuth. From the OpenID spec:
OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It enables Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
Combining these two protocols together allows for centrally managing users to use resources either hosted by your infrastructure or third-parties. This foundation enables Single Sign-On (SSO) and allows for users to sign-in to multiple services using one account, such as their Google, Microsoft, Apple, or Facebook account.
Basic Setup
Now that we have gone over the basics of what OAuth 2 and OpenID are, let’s actually do something. I am doing this project on a FreeBSD 15.0 machine, however, the service is written in Java and should be fairly portable across operating systems. After install of OS, install dependencies and download the application files:
# Dependency for Keycloak
pkg install -y openjdk21
fetch https://github.com/keycloak/keycloak/releases/download/26.5.0/keycloak-26.5.0.zip
cd keycloak-26.5.0
bin/kc.sh start-dev
Now visit the IP of the server at port 8080, for example my server is
located at 192.168.122.146, so I would go to
http://192.168.122.146:8080. We unfortunately run into a problem here,
when running the kc.sh script to setup the admin account it
complains if we are not accessing it over localhost, which I am not in
this case. There are a few different ways to handle this, but the
simplest is going to forward X11 over ssh and run Firefox (or another
browser) on the server. I explain this process in this blog post.
Additionally, you will probably want to open multiple ssh sessions or
use a terminal multiplexer such as screen to more
easily run the kc.sh script and a remote Firefox.
Now that we have are remote Firefox setup and can access the web
interface over localhost, let’s try re-running the kc.sh
script:
cd keycloak-26.5.0 # Only necessary if not already in the directory
bin/kc.sh start-dev
After creating the initial admin account, we can start managing the ‘realms’, which is Keycloak’s term for what might be call a tenant in something like Microsoft Entra. This process is documented decently well in the getting started guide, however a quick rundown is:
- Login to the Admin Console
- Click “Manage Realms” and “Create realm” at the top
- Then fill out the required information for the realm
- Click the newly created realm to make it the “Current realm”
From there, we can go to the ‘Realm Settings’ tab and modify the permissions and whatnot for the realm. For now, I am going to leave the realm settings alone, but this is where we would set things like allowing user registration. I am also going to go ahead and create some test users for when it is time to test out using Keycloak as an authentication mechanism. To do that, make sure the newly created realm (remember the ‘Master’ realm is intended to be used for administrative purposes, not for client use) is active, and go to ‘Users’ in the left sidebar and ‘Create new user’. Enter the user’s information (username is the only required field), then after the user is created click on ‘Credentials’ in the top banner. This is where we can set a password for the user to be able to authenticate; we can then try to login to the realm using that new account. To get to the realm’s console (which is different than the admin console so going to http://${DOMAIN}:8080 won’t work) is located at:
http://DOMAIN : 8080/realms/{REALM}/account
Where ${DOMAIN} is the domain name or IP address of the
Keycloak server and ${REALM} is the name of the realm that
was just created. It would also be beneficial for later steps to setup
some sort of name resolution for the Keycloak server. This can be done
pretty easily by adding an entry to /etc/hosts on Unix-like
OSs or in the C:\Windows\system32\drivers\etc\hosts file in
Windows. Alternatively, setting up DNS for the server would work as well
(but is outside the scope of this project). Additionally, setting up
name resolution on the server that will be using Keycloak will also be
important later on.
After confirming we can authenticate into our newly created realm, let’s setup a connection to be able to use this account to access other resources.
Authenticating Forgejo with Keycloak
Now we can finally do something cool and setup authentication to a service using Keycloak. For testing this, I decided to go with Forgejo as it is fairly easy to setup and has a nice interface for connecting external authentication services. I’m not going to go through the setup of Forgejo itself, just install like normal and create the initial admin account so we can login and start working with the service.
We are then going to go back over to Keycloak and setup an
Keycloak has a tool for testing the authentication, however, it’s much more fun to set up an application and see it work with Keycloak as an authentication method. Before we can actually integrate Keycloak as an authentication service though, we need to setup the OpenID client for it in Keycloak. To set up that client, login to Keycloak and select the realm that was created earlier to make it the active realm, then click ‘Clients’. Click the ‘Create client’ button which will take you into a menu to assist with creating that new client. You are only required to select a client type and give the client an ID, however, it is usually best practice to also give at least a description to assist future admins in understanding what that client does. In this instance, we will leave the client type as ‘OpenID Connect’ and give the client a simple name such as forgejo, then click next.
The next screen will assist us in configuring the specific capabilities for the client. This can determine which features of the OAuth standard that this client will be capable of using for authorization. The only default setting we need to change here is turning the client authentication on, this will allow us to generate a client secret to add to Forgejo to be able to authenticate users. We also need to choose a Proof Key for Code Exchange (PKCE) method, I went with S256 as it is more secure and there is not really a reason to go with the less secure option here.
At the login settings screen, we tell Keycloak more about the service that will be using on the client, i.e. telling Keycloak about Forgejo. Enter the root and home url, in my case both are: http://192.168.122.99:3000, then we also need to enter the “Valid redirect URIs” and “Valid post logout redirect URIs”. Forgejo will tell you what these should be, so let’s go back into Forgejo with out previously created admin account and begin the setup process over there.
Once you get into Forgejo as an Admin go to your profile → Site administration → Identity & access → Authentication sources and ‘Add authentication source’. Change the authentication type to OAuth2 and the OAuth2 provider to OpenID Connect, the menu will start asking for some information. Ignore that for now, and scroll to the bottom and grab the callback/redirect URL under the ‘OAuth2 authentication’ section. The line you are looking for should read:
When registering a new OAuth2 authentication, the callback/redirect URL should be: http://192.168.122.99:3000/user/oauth2//callback
With a potentially different URL of course.
Copy and paste that into the “Valid redirect URIs” and “Valid post
logout redirect URIs” boxes. Once that is finished, click save and you
will be redirected to the newly made client’s OpenID entry. In that
menu, we can modify the settings we previously set if we want to change
something, however, what we are currently wanting is under the
‘credentials’ tab. This tab is where we get our secret to be able to
authenticate Forgejo as something that should be able to use Keycloak
for user information. Copy the secret and paste it into the ‘Client
secret’ in the Forgejo authentication setup menu, additionally type or
paste the client ID that we created in the Client ID text field. Finally
we need to add the OpenID Connect Auto Discovery URL. This URL is found
at the bottom of the ‘Realm settings’ menu, the one we want is the
‘OpenID Endpoint Configuration’; a big gotcha I ran into was not having
the domain name for the Keycloak server properly configured on the
Forgejo server. That is why I recommended putting an entry in
/etc/hosts, at least until we get Keycloak in a more
production ready state. Paste that URL into Forgejo as the Auto
Discovery URL, then make sure the authentication source is active and
‘Add authentication source’.
From there, simply try to sign-in using the previously created Keycloak user. Assuming it works, we have a very basic, not production ready OAuth 2 server. The next post I plan on writing about how to get Keycloak in a more production ready state and potentially how to get passkeys to work.
Resources
OAuth and OpenID feel like a massive topic and running a server that provides those things certainly feels like it takes quite a bit to get it right. If this is something that you are seriously wanting to do, I recommend doing some learning on not only the Keycloak service, but also the underlying protocols. From there, start by creating a test lab before putting it into production because there have been quite a few things that I am still working on figuring out, such as how to create admin users for a realm rather than doing everything via the master realm.
For those interested in learning more, here are some resources that I have used to learn about this and related topics.
- Auth0 - This site provides a very entry level overview of the topics and how they piece together. It also provides some references on deeper learning for people that want more.
- Keycloak Getting Started - Getting started guide that I have loosely been following to get this working
- Keycloak’s landing page for guides