Some time ago, we needed a solution for Single Sign On authentication between multiple web services. At least at that time, we thought the OpenID protocol was too complicated, and we were not sure about its Ruby on Rails plugins. Therefore, we developed our own protocol, instead of introducing OpenID providers and OpenID users.
I have two questions:
Was it bad not to create our own OpenID provider and configure our OpenID consumers accept only this? Public login or registration is not allowed, and we like to keep authentication simple.
Can you spot a critical error or vulnerability in the next design?
If you, as a commune, can approve this project, I will consider extracting this code into the Ruby on Rails plugin.
Please review the flowchart and sequence of the sequence .
Details:
Authentication Provider ("AP"):
- A central service that contains all user data.
- In this setting, there is only one “access point”.
- It may be possible to have multiple “APs,” but this should not be relevant in this context.
- "AP" knows every "S" in advance.
Authentication Client (Service "S"):
- There are several internal and external web services.
- Each service knows the "AP" and its public key in advance.
Actor ("A"):
- The end user who authenticates herself with the AP using a username and password
- Can request directly any URI "S" or "AP" before it logs in.
Connections between "A", "S" and "AP" are protected by HTTPS.
The authentication logic is briefly described:
This is a description of the graphic flowchart and sequence diagram that were linked at the top of this message.
1) Supplier "AP"
- "AP" makes an HTTP POST request from server to server "S" to receive nonce.
- "AP" generates an authentication token.
- Authentication current is an XML object that includes:
- validity period (after 2 minutes),
- previously requested nonce (to prevent replay),
- identification name "S" (token for Service_1 is not suitable for Service_2),
- end user information.
- The authentication current is encrypted using AES256, and the encryption key and initialization vector are signed with the private RSA key of the AP.
- The resulting strings ("data", "key" and "iv") are first encoded by Base64, and then URL encoded so that they can be delivered to the URL query string.
- End user "A" is redirected via HTTP to service "S" (HTTPS GET request).
2) Service "S"
- Gets the authentication token in the URL parameters from the user agent.
- Decrypts an authentication token with the public public key AP.
- Accepts only one authentication token only once (the token includes unce, which is valid only once).
- Verifies that the authentication name in the authentication token matches the service name.
- Verifies that the authentication token has not expired.
Notes:
This is not a problem if someone else can decrypt the authentication token, as it does not contain confidential user information. However, it is very important that no one but the AP can create a valid authentication token. Therefore, an RSA key pair is involved.
The RSA private key is used only for signing the token, because it cannot encrypt data that is longer than the actual key length. Therefore, AES is used for encryption.
Since the authentication token is supplied as an HTTP GET request, it will be saved, for example. in the Apache log file. The use of one-time inaction and expiration dates should minimize the possibility of a second attack. A POST request will require an HTML page with a form that is automatically submitted by Javascript, so GET is used.
Service "S" generates nonce only in a request for the server-server API. Therefore, unauthenticated requests should not present a DoS vulnerability.