Ferrum is a NextGen Auth Server (almost)

evillord666

Ushakov Michael

Posted on September 29, 2022

Ferrum is a NextGen Auth Server (almost)

Authorization and Why we created Ferrum project

Nowadays, almost all distributed, web or client-server systems have authorization to restrict access only to those users who have rights,OAuth2.0 and OpenId-Connect are most popular and convenient technologies for users authorization. There are some well-known authorization servers, i.e. KeyCloak.

Personally, I like KeyCloak and use it where it is possible. About 2 years ago I had to expand my technology stack and I started to write apps using Golang. These apps are WEB API that use Authorization on KeyCloak server. When API grows and become more complicated, it is very important to make automated integration tests. For authorization we using separate from demo/production server, and it is important to make code portable to any machine, therefore the most convenient way is to start all infrastructure before any test and stop when test ends with any persistent data cleanup at the end. Such approach can't be easily and fast implemented using Keycloak. Therefore, we created Ferrum authorization server that could be run and stopped from code and with API compatible with KeyCloak (with similar behavior and endpoints). Ferrum could be used not only for tests purposes but also like a full-fledged authorization server.

This is a tutorial how easily (in 1-2 minutes) add authorization to your solutions or how to write integrational tests on golang web api also in 1-2 min.

Build, configure, and run authorization server

To use Ferrum as a standalone authorization server, you need:

  1. Build it with go build (optionally re-generate SSL Certificates using go generate);
  2. Edit very simple config.json file and specify application endpoint;
  3. Configure you server realm, clients and users data (for more details see next paragraph) in data.json file (in future we'll add store data in NO SQL and user register feature);
  4. Run executable ./Ferrum, you authorization server is up. That's all.

Configure server data

Let's consider data configuration more detailed. Data is similar to what we are having on Keycloak. We have 3 types of objects for version 0.1.0:

  • realm = organization or solution
  • client = project / application inside realm;
  • user;

Every realm could contain as many clients and users as you wish. Realm structure is:

{
    "name": "myapp",
    "token_expiration": 330,
    "refresh_expiration": 200,
    "clients": [
     {
         "id": "d4dc483d-7d0d-4d2e-a0a0-2d34b55e5a14",
         "name": "test-service-app-client",
         "type": "confidential",
         "auth": {
                     "type": 1,
                     "value": "fb6Z4RsOadVycQoeQiN57xpu8w8wplYz"
         }
     } ],
     "users": [
      {
          "info": {
              "sub": "667ff6a7-3f6b-449b-a217-6fc5d9ac0723",
              "email_verified": false,
              "roles": [
                  "admin"
               ],
               "name": "admin sys",
               "preferred_username": "admin",
               "given_name": "admin",
               "family_name": "sys"
          },
          "credentials": {
               "password": "1s2d3f4g90xs"
          }
     }
     ]
Enter fullscreen mode Exit fullscreen mode

Restrictions:

  1. For data compatibility with Keycloak we must have "info" struct in every user with preferred_username property as a login;
  2. We must have credentials struct and password property for a password check, in future we will store only password hashes, and won't store opened passwords.

Advantages:

  1. We could have userinfo of any complexity (info structure in every user, see upper json) and we should not make some changes in the code. I.e. we like to add phone number, easy:
   {
       "info": {
           "phone": "+79996663322",
           // other props ...
       },
       "credentials": {
            // other props ...
       }
   }
Enter fullscreen mode Exit fullscreen mode
  1. Simple configuration, it enough 1-2 minutes to prepare server data and use;
  2. Could be running from other application for tests purposes or as part of development / production solution.
  3. API compatible with KeyCloak, you could easily switch to one technological stack in future when Ferrum will have all necessary features.

Ferrum REST API

For version v0.1.0 we have only 2 endpoints:

  1. For issuing a new token {baseUrl}/auth/realms/{realm}/protocol/openid-connect/token/
  2. For getting user info that issued this token {baseUrl}/auth/realms/{realm}/protocol/openid-connect/userinfo/

All these REST API

Write integrational tests with go

A full working example could be found in our integation test here
Minimal code example:

// 1. init app, appConfig, testServerData and testKey are variables
app := CreateAppWithData(appConfig, &testServerData, testKey)
// 2. start the app
res, err = app.Start()
// 3. issue new token (taken from func)
// baseUrl, realm and other token issue params are variables
tokenUrlTemplate := "{0}/auth/realms/{1}/protocol/openid-connect/token/"
tokenUrl := stringFormatter.Format(tokenUrlTemplate, baseUrl, realm)
getTokenData := url.Values{}
getTokenData.Set("client_id", clientId)
getTokenData.Set("client_secret", clientSecret)
getTokenData.Set("scope", "profile")
getTokenData.Set("grant_type", "password")
getTokenData.Set("username", userName)
getTokenData.Set("password", password)
response, err := http.PostForm(tokenUrl, getTokenData)
// 4. get a token and use
responseBody, err := io.ReadAll(response.Body)
var result dto.Token
err = json.Unmarshal(responseBody, &result)
stationEndpointTemplate := "{0}/api/station/"
getStationsUrl := stringFormatter.Format(stationEndpointTemplate, baseServiceUrl)
client := http.Client{}
request, err := http.NewRequest("GET",getStationsUrl, nil)
    request.Header.Set("Authorization", "Bearer "+ result.AccessToken)
response, err := client.Do(request)
// todo(UMV): parse response
// 5. stop app
app.Stop()
Enter fullscreen mode Exit fullscreen mode

Conclusion

Thanks for reading, please help our project grow, please add us a github star
Our project will grow anyway, we are planning to add:

  • data store in one of NoSQL databases;
  • secure credential store;
  • integrations with external users provider like LDAP;
  • integrations with social providers like google;
  • possibility to authorize using biometrical data.
💖 💪 🙅 🚩
evillord666
Ushakov Michael

Posted on September 29, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related