This is an implementation of an SSO for DevClub sites. A web server hosted at auth.devclub.in
handles the authentication part for each service on DevClub domains and allows a user to stay signed in across multiple services.
Our model uses a single SSO JWT token generated by the auth server which is stored in a cookie and will be sent to all subdomains of devclub.in
Payload
"user":{
"id": <uid>,
"firstname": <First Name>
"lastname": <Last Name>
"email": <Registered Email>
"roles": [<list of role strings> ]
"is_verified" : A boolean specifying whether the user is verified
}
Signature We utilize a RSA private key at the auth server to sign the payload and the public key is distributed to the SSO client at the time of registration.
- HttpOnly - This ensures that the cookie is inaccessible frontend javascript and hence immunises us against XSS attacks
- Secure - Enables transmission over SSL only.
- SameSite - Lax, Enables transmission of cookies from
a.devclub.in
tob.devclub.in
- max-age -
900s
These features hence rely on the browser to do all the cookies handling part and hence nothing has to be changed in the front-end code of any client.
A client will have to register with the SSO before using the service. The registration can be done at auth.devclub.in/client/register
.
Note: This endpoint will be accessible only to devclub members.
The client will give its basic details like the subdomain name, and they can also specify what additional roles are required by them, to implement specific access control for their subdomain and what filter should be applied to check if a particular user is entitled for this role.
For example, yearbook
when registering with the SSO gives the following information to the SSO server.
- SUBDOMAIN =
yearbook
- ROLE_NAME =
yearbook_role
- ROLE_FILTER =
{fname:<regexp>,lname:<regexp>,entry_num:<regexp>...}
The ROLE_FILTER
will be an object with regexp to match for each field in the user model. Only when all regexp are matched, the user will be given this role.
Note:
- The Client can specify multiple roles as long as the role_names do not conflict with existing role names in the db.
- Alternatively, we can keep
ROLE_FILTER
to be an arrow function that takes the user model as input and returnsTrue/False
after verifying if the user is entitled to the role or not. However to implement this method we will have to take care of input sanitisation since it can lead to privilege escalation, once a single devclub member account is compromised.
As soon as the client is finished giving the information, the auth server will run the ROLE_FILTER
regexps on each user and if the regex match, the ROLE_NAME
will be appended to the roles field of the user model.
The SSO server returns some variables for the client to store in its .env
file.
- Universal Role Strings (like regular_user, iitd_user, admin, devclub_member, etc.)
- Service specific custom Role Strings - The Role strings that the client had requested at the time of registration
- The Server's public key - to decrypt the SSO token and hence verify it.
- The user visits
yearbook.devclub.in
. - Middleware -
- The middleware at
yearbook.devclub.in
checks for the presence of atoken
in the cookie. - If this is found the middleware verifies the token using the public key provided by SSO.
- After verification it checks if the Access Control Role specified by
yearbook
at the time of its registration with SSO server is present in the payload data or not. Note: The role strings that need to be checked for in the payload will have to be provided to the middleware function as parameters. Hence this needs to be specified by the client that which roles does it need to check for authenticating the user. The role strings can be present in the .env file at the client. They are provided by the auth server during the registration of the client with SSO. - Refresh - Next it checks if ttl for cookie is less than 1 min, if so then it sends a request to
auth.devclub.in/refresh-token
with the present access token and the auth server sends a response with Set-Cookie header to a new access-token. - If all the above checks pass the user is authenticated, and if not then the user is redirected to
auth.devclub.in/login/?serviceURL=yearbook.devclub.in
- The middleware at
- SSO Server login - At the login page of
auth.devclub.in/login/?serviceURL=<service_url>
the user enters the credentials for logging in, and after the credentials are verified in the database, the SSO service sends a response to set a Cookie with the SSO token that is signed using the private key that only the SSO server holds. It then redirects the user back to theserviceURL
in the query parameter.
- The user accesses say
study.devclub.in
- The browser sends the
token
cookie tostudy.devclub.in
(since SameSite=Lax) and the middleware library performs the same functions as outlined in the section Login and accessing
Handled by middleware by sending request to auth.devclub.in/refresh-token
when the ttl is less than 1 min. This is outlined in Refresh by middleware
The middleware deletes the cookie from the browser by sending a Set-Cookie header with a cookie that has expired in the past. This effectively logs the user out of all the services on DevClub.
When doing a password change, the old password and new password will be asked, when the request succeeds the user will be logged out and redirected to auth.devclub.in/user/login
.
Since the user is not logged in, hence the basic procedure of first checking the email in the database and then sending a password reset verification link at the specified link is to be followed. Once the user resets the password, it will be redirected to auth.devclub.in/user/login
We have utilized MongoDb for our database.
The model for the db is as follows: Table 1 - User Model
user = {
unique_id
name, email and other metadata
password_hash,
roles: ManytoMany relationship to "Role" model,
is_verified: <boolean>
}
If someone registers with an email, then we have a verification system, where a verification email is sent at the email-address.
Until the email-address is verified is_verified
is set to False, and only upon verification is_verified
is set to True.
All log-in requests for is_verified=False
users are not authenticated, unless the account is verified.
Table 2: Role
role:{
role_name: <string>,
regex_dict: {fname:<regexp>,lname:<regexp>,entry_num:<regexp>,...}
}
Table 3: SSO Client
services = {
subdomain: <string>
custom_roles: Many to Many relationship to model Role
}
Table 4: Social Accounts
social = {
provider: <string>,
email: <string>,
primary_account: Foreign Key to User model
}
-
Whenever Social Auth is used to login, - first the
tuple(provider,email)
is checked to see if it already exists in thesocial
model objects. - If an entry is found the user is logged in with the matching object'sprimary_account
field - Else a new db entry for a primary account (User model) will also be created with the default role ofregular_user
and this user object will be set in theprimary_account
field of the social accounts object. -
However when the user is already logged in and asks for linking their account with a social account, a new db entry for only the social account model will be created and the
primary_account
will be set to the user object that requested linkage of accounts.
- Only a single token is used and hence this reduces the hassle of managing different tokens for each service, hence creating problems in logout. In this model once logged out the user gets logged out of all the services since there is a global dependency on the
sso_token
- The roles field in the payload can take care of both the public access control and the private access control rules. A client can choose to use the universal roles (like regular_user, iitd_user etc.) while invoking the middleware (which will be the case most of the times), however some websites like
yearbook
may require additional access control methods (like allowing only final year students). These can be specified during the registration as they are some additional roles relevant only foryearbook
. Hence if in future any new service of devclub comes up and it requires some access control rules, they can follow this doc to get themselves registered to the SSO and have their private access control managed by the SSO server.