OneLogin ruby client how to identify the user's nameID - ruby

I am using the OneLogin SAML Test Connector as a simulated IDP with the ruby-saml gem and the supplied sample program. That's working fine.
In my real-world app, we are the SP and there are multiple identity providers, each of which will have multiple users who need to SSO to our app.
I have observed that in the sample app, my AuthnRequest does not define the user's nameID (email address) anywhere, but instead it is appended to the IDP URL in the form of a numeric id. e.g. https://app.onelogin.com/saml/metadata/5551212 where app.onelogin.com identifies the IDP and 5551212 identifies the "user". I realize that when onelogin is the IDP the "user" is actually an app id, but in our case we want it to identify an actual user, not an app.
Is it correct that the AuthnRequest does not contain a nameID element?
Should I identify the user in the IDP's URL using a numeric ID? The numeric id would have to be shared offline between the parties, and would have to be transmitted in the initiating URL. I like this approach because it doesn't expose any identifying information in the URL, e.g. an email address.
Should I identify the user by Base64-encoding the email address and appending that to the URL? This is less secure, but it might be ok since the transmission will always be encrypted with SSL. And it avoids the offline handshake and sharing a numeric ID.

AuthnRequest does not define the user's nameID (email address)
anywhere, but instead it is appended to the IDP URL in the form of a
numeric id.
This is not true, that numeric id identify internally the IdP, not the user.
Yes, check the SAML standard, saml:Subject is optional. And SAML2int profile says that AuthNRequest message MUST NOT contain a saml:Subject.
No
No
You don't need to pass user information to the IdP, you should simply send an AuthNRequest to the SSO endpoint and a form to place a username/password gonna be showed to the user (if already exists an active session this authentication is not required. Then a SAMLResponse will be send back to the SP's Assertion Consumer Service endpoint.

Correct. Check out https://www.samltool.com/base64.php to help with looking at Response attributes.

Related

Understanding the authorization part of JWT and session

So i read about how authentication is done using JWT, where we basically verify if the token is valid using a private key (assuming RSA is the algortihm). And if the token is valid, then the user is considered authenticated. And I also read about session authentication where we check if the user supplied session id (through cookie), exist in the session store (assuming mysql / redis is used to store session). If it exist, then the user is considered authenticated.
But how do we use JWT and session for authorization ? Lets consider an action such as GET invoice , whereby a user can only view the invoice that he owns.
If we consider the user is already authenticated,
how do we check if the user is authorized if we are using JWT?
And how do we do it for session ?
You are probably confusing the things. One of the benefits using JWT is to avoid maintaining sessions which is big bottle neck in scaling.
JWT (Json Web Token) carry all the information that would require it to get authenticated, you don't need to maintain the session. Every single HTTP request to server will carry JWT that would contain necessary user claims including the signature. On server you will validate the signature to verify if the token is valid or not. You can verify the token without maintaining any session and there are number of ways to do it.
Since JWT is essentially a Json object (Header, Body , Signature) , you can put in claims (emails, roles, profile etc) in JWT body. Once you verify the token , you can extract the claims including any roles and check if user is authorized to access the resource.
You must look into Open ID Connect and Tokens here

Onedrive OAuth 2.0 code flow for getting access token 'redirect uri' is not specified in the list of urls specified

Before adding, yes it works when I give the entire url like http://localhost:8080/onedrive/oauth2/success/1 in the list of uri in azure uris. I am using code flow to authroize these tokens.
But as per the docs, it should work with me just mentioning the domain name there, like http://localhost:8080. Which it doesn't.
I want to do something like send the user id along with every request for me to keep track of which user I should link this accees token to, and have no idea to do so, if this issue is there. My current application logic is, when my application sends the user details and calls my spring API, I want to handle all these transfer of tokens in the server side, so I want to transfer this userId as my path variable. How do I go about doing this? Has anyone done this, can they explain to me any other different solution?
You can't add custom details to OAuth redirects and it is best practice to always register the full redirect uri.
In terms of tracking the user, after login the token has a user id and you can also get fields such as user name and email - so both the UI and API will know which user each token is for. I can provide further details on mechanics if needed.
The user id in a token is often a generated value, whereas the user id you want to use in API path segments is maybe a user id from your app's back end database - if so you will need to map between token details and database details.
If you provide redirect uri as http://localhost:8080/ then it means you are handling the api response in
/
endpoint and not
/onedrive/oauth2/success/1
To get to know the user to whom you are linking, few ideas which you can use are
1) Use security to obtain the logged in user credentials (Ex: Principal if you're using Spring security in java)
2) After successful authentication, use the user id you have and send one more request to backend and store it database with userid as a key

JWT and Session: how JWT should be properly used instead of Session

I am working on a project with PHP and angular. For the user sign in, we're using JWT. Still can't understand why we should use JWT instead of Sessions if each time the user browse a component we need to send the token to server code to check if the user still signed in or not.
Username and password will be sent to server code, where the authentication process will happen, and then generate a token and send it back to angular then save at the local storage.
Any comment on how JWT should be properly used.
EDIT
My question is about the process of checking the JWT when user surf the site and go from component into another.
If you use session for your application... Then while horizontal scaling sharing the session data becomes a burden ....you either need a specialised server .. Jwt are stateless and have no such requirement. It contain following data
Header - information about the signing algorithm, the type of payload (JWT) and so on in JSON format
Signature - well... the signature
Payload - the actual data (or claims if you like) in JSON format
Your JWT already is a proof of your authentication. So you have to send it with each request but you can simplify the authentication logic on server-side.
While on the login you will have to check the credentials you can rely on the JWT's signature and expiryDate. If the signature is still correct the token is valid and you do not have to authenticate anymore.
So regarding your horizontal authentication.
If the called service needs to be authenticated you have to check the JWT for validity on each request (normally works reasonably fast). If there are open api calls you can of course ignore the JWT on server side.
At the end of the day there is no difference to your "session" which will also send some "secret" key which maps your session context. Therefore, it will also be validated.
For some backends you can also use the JWT as your session key to get both worlds involved.
Example:
lets say you have two api roots:
api/secured/*
api/open/*
(Note that the secured and open are only here for demonstrative purposes)
The secured part will contain all the services you want to be authenticated.
The open part can contain insensitive data as well as your login services:
api/open/login -> returns your token
api/open/token/* -> refresh, check re-issue whatever you might need
So now lets say the user accesses your site. You will want to provde an authentication error if he tries to access any api/secured/* URL without a proper JWT.
In this case you can then redirect him to your login and create a token after authenticating him.
Now when he calls an api/secured/* URL your client implementation has to provide the JWT (Cookie, Request header, etc...).
Depending on your framework, language etc. you can now provide an interceptor/filter/handler on server side which will check:
If the JWT is present
if the signature is valid (otherwise the token was faked)
if the JWT is still valid (expiryDate)
Then you can act accordingly.
So to sum up:
There is no need to "authenticate" unless you want to create a new token.
In all other cases it is enough to check the validity of your JWT

Token based authentication in REST APIs

I trying to implement a token based authentication approach:
Every successful login creates new token.
If user selects "keep me logged in" or the user is using a mobile device, the token is persisted in a Redis database without an expiration date. Otherwise, the token will expire in 20 minutes.
Once user is authenticated, the token is checked from each subsequent request in my Redis database.
I'm wondering how I can identify devices. In case of mobile devices, I can use a device identifier. But how can I identify a browser?
Example: The user logs in using Chrome and selects "keep me logged in". A token is generated and persisted with the browser name in Redis. If the user logs in from Firefox, saves the token and "Firefox" in the database. I save the token in Redis whereas token is created on successful authentication. Is it fine to persist only the token and the browser where the token is being used? Or do I need to persist the IP as well?
Additional question: How to avoid attackers to steal the token from a cookie?
How token-based authentication works
In a few words, an authentication scheme based on tokens follow these steps:
The client sends their credentials (username and password) to the server.
The server authenticates the credentials and generates a token.
The server stores the previously generated token in some storage along with the user identifier and an expiration date.
The server sends the generated token to the client.
In every request, the client sends the token to the server.
The server, in each request, extracts the token from the incoming request. With the token, the server looks up the user details to perform authentication and authorization.
If the token is valid, the server accepts the request.
If the token is invalid, the server refuses the request.
The server can provide an endpoint to refresh tokens.
How to send credentials to the server
In a REST applications, each request from client to server must contain all the necessary information to be understood by the server. With it, you are not depending on any session context stored on the server and you do not break the stateless constraint of the REST architecture defined by Roy T. Fielding in his dissertation:
5.1.3 Stateless
[...] each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. [...]
When accessing protected resources that require authentication, each request must contain all necessary data to be properly authenticated/authorized. It means the authentication will be performed for each request.
Have a look at this quote from the RFC 7235 regarding considerations for new authentication schemes:
5.1.2. Considerations for New Authentication Schemes
There are certain aspects of the HTTP Authentication Framework that
put constraints on how new authentication schemes can work:
HTTP authentication is presumed to be stateless: all of the
information necessary to authenticate a request MUST be provided
in the request, rather than be dependent on the server remembering
prior requests. [...]
And authentication data (credentials) should belong to the standard HTTP Authorization header. From the RFC 7235:
4.2. Authorization
The Authorization header field allows a user agent to authenticate
itself with an origin server -- usually, but not necessarily, after
receiving a 401 (Unauthorized) response. Its value consists of
credentials containing the authentication information of the user
agent for the realm of the resource being requested.
Authorization = credentials
[...]
Please note that the name of this HTTP header is unfortunate because it carries authentication data instead of authorization. Anyways, this is the standard header for sending credentials.
When performing a token based authentication, tokens are your credentials. In this approach, your hard credentials (username and password) are exchanged for a token that is sent in each request.
What a token looks like
An authentication token is a piece of data generated by the server which identifies a user. Basically, tokens can be opaque (which reveals no details other than the value itself, like a random string) or can be self-contained (like JSON Web Token):
Random string: A token can be issued by generating a random string and persisting it to a database with an expiration date and with a user identifier associated to it.
JSON Web Token (JWT): Defined by the RFC 7519, it's a standard method for representing claims securely between two parties. JWT is a self-contained token and enables you to store a user identifier, an expiration date and whatever you want (but don't store passwords) in a payload, which is a JSON encoded as Base64. The payload can be read by the client and the integrity of the token can be easily checked by verifying its signature on the server. You won't need to persist JWT tokens if you don't need to track them. Althought, by persisting the tokens, you will have the possibility of invalidating and revoking the access of them. To keep the track of JWT tokens, instead of persisting the whole token, you could persist the token identifier (the jti claim) and some metadata (the user you issued the token for, the expiration date, etc) if you need. To find some great resources to work with JWT, have a look at http://jwt.io.
Tip: Always consider removing old tokens in order to prevent your database from growing indefinitely.
How to accept a token
You should never accept expired tokens or tokens which were not issued by your application. If you are using JWT, you must check the token signature.
Please note, once you issue a token and give it to your client, you have no control over what the client will do with the token. No control. Seriously.
It's a common practice to check the User-Agent header field to tell which browser is being used to access your API. However, it's worth mention that HTTP headers can be easily spoofed and you should never trust your client. Browsers don't have unique identifier, but you can get a good level of fingerprinting if you want.
I don't know about your security requirements, but you always can try the following in your server to enhance the security of your API:
Check which browser the user was using when the token was issued. If the browser is different in the following requests, just refuse the token.
Get the client remote address (that is, the client IP address) when the token was issued and use a third party API to lookup the client location. If the following requests comes an address from other country, for example, refuse the token. To lookup the location by IP address, you can try free APIs such as MaxMind GeoLite2 or IPInfoDB. Mind that hitting a third party API for each request your API receives is not a good idea and can cause a severe damage to the performance. But you can minimize the impact with a cache, by storing the client remote address and its location. There are a few cache engines available nowadays. To mention a few: Guava, Infinispan, Ehcache and Spring.
When sending sensitive data over the wire, your best friend is HTTPS and it protects your application against the man-in-the-middle attack.
By the way, have I mentioned you should never trust your client?
Once server is receives the request from the client, it contains the User-Agent. This attribute will help us to identify the client.
Please refer this link: How do I detect what browser is used to access my site?

REST authentication and exposing the API key

I've been reading up on REST and there are a lot of questions on SO about it, as well as on a lot of other sites and blogs. Though I've never seen this specific question asked...for some reason, I can't wrap my mind around this concept...
If I'm building a RESTful API, and I want to secure it, one of the methods I've seen is to use a security token. When I've used other APIs, there's been a token and a shared secret...makes sense. What I don't understand is, requests to a rest service operation are being made through javascript (XHR/Ajax), what is to prevent someone from sniffing that out with something simple like FireBug (or "view source" in the browser) and copying the API key, and then impersonating that person using the key and secret?
We're exposing an API that partners can only use on domains that they have registered with us. Its content is partly public (but preferably only to be shown on the domains we know), but is mostly private to our users. So:
To determine what is shown, our user must be logged in with us, but this is handled separately.
To determine where the data is shown, a public API key is used to limit access to domains we know, and above all to ensure the private user data is not vulnerable to CSRF.
This API key is indeed visible to anyone, we do not authenticate our partner in any other way, and we don't need REFERER. Still, it is secure:
When our get-csrf-token.js?apiKey=abc123 is requested:
Look up the key abc123 in the database and get a list of valid domains for that key.
Look for the CSRF validation cookie. If it does not exist, generate a secure random value and put it in a HTTP-only session cookie. If the cookie did exist, get the existing random value.
Create a CSRF token from the API key and the random value from the cookie, and sign it. (Rather than keeping a list of tokens on the server, we're signing the values. Both values will be readable in the signed token, that's fine.)
Set the response to not be cached, add the cookie, and return a script like:
var apiConfig = apiConfig || {};
if(document.domain === 'example.com'
|| document.domain === 'www.example.com') {
apiConfig.csrfToken = 'API key, random value, signature';
// Invoke a callback if the partner wants us to
if(typeof apiConfig.fnInit !== 'undefined') {
apiConfig.fnInit();
}
} else {
alert('This site is not authorised for this API key.');
}
Notes:
The above does not prevent a server side script from faking a request, but only ensures that the domain matches if requested by a browser.
The same origin policy for JavaScript ensures that a browser cannot use XHR (Ajax) to load and then inspect the JavaScript source. Instead, a regular browser can only load it using <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123"> (or a dynamic equivalent), and will then run the code. Of course, your server should not support Cross-Origin Resource Sharing nor JSONP for the generated JavaScript.
A browser script can change the value of document.domain before loading the above script. But the same origin policy only allows for shortening the domain by removing prefixes, like rewriting subdomain.example.com to just example.com, or myblog.wordpress.com to wordpress.com, or in some browsers even bbc.co.uk to co.uk.
If the JavaScript file is fetched using some server side script then the server will also get the cookie. However, a third party server cannot make a user’s browser associate that cookie to our domain. Hence, a CSRF token and validation cookie that have been fetched using a server side script, can only be used by subsequent server side calls, not in a browser. However, such server side calls will never include the user cookie, and hence can only fetch public data. This is the same data a server side script could scrape from the partner's website directly.
When a user logs in, set some user cookie in whatever way you like. (The user might already have logged in before the JavaScript was requested.)
All subsequent API requests to the server (including GET and JSONP requests) must include the CSRF token, the CSRF validation cookie, and (if logged on) the user cookie. The server can now determine if the request is to be trusted:
The presence of a valid CSRF token ensures the JavaScript was loaded from the expected domain, if loaded by a browser.
The presence of the CSRF token without the validation cookie indicates forgery.
The presence of both the CSRF token and the CSRF validation cookie does not ensure anything: this could either be a forged server side request, or a valid request from a browser. (It could not be a request from a browser made from an unsupported domain.)
The presence of the user cookie ensures the user is logged on, but does not ensure the user is a member of the given partner, nor that the user is viewing the correct website.
The presence of the user cookie without the CSRF validation cookie indicates forgery.
The presence of the user cookie ensures the current request is made through a browser. (Assuming a user would not enter their credentials on an unknown website, and assuming we don’t care about users using their own credentials to make some server side request.) If we also have the CSRF validation cookie, then that CSRF validation cookie was also received using a browser. Next, if we also have a CSRF token with a valid signature, and the random number in the CSRF validation cookie matches the one in that CSRF token, then the JavaScript for that token was also received during that very same earlier request during which the CSRF cookie was set, hence also using a browser. This then also implies the above JavaScript code was executed before the token was set, and that at that time the domain was valid for the given API key.
So: the server can now safely use the API key from the signed token.
If at any point the server does not trust the request, then a 403 Forbidden is returned. The widget can respond to that by showing a warning to the user.
It's not required to sign the CSRF validation cookie, as we're comparing it to the signed CSRF token. Not signing the cookie makes each HTTP request shorter, and the server validation a bit faster.
The generated CSRF token is valid indefinitely, but only in combination with the validation cookie, so effectively until the browser is closed.
We could limit the lifetime of the token's signature. We could delete the CSRF validation cookie when the user logs out, to meet the OWASP recommendation. And to not share the per-user random number between multiple partners, one could add the API key to the cookie name. But even then one cannot easily refresh the CSRF validation cookie when a new token is requested, as users might be browsing the same site in multiple windows, sharing a single cookie (which, when refreshing, would be updated in all windows, after which the JavaScript token in the other windows would no longer match that single cookie).
For those who use OAuth, see also OAuth and Client-Side Widgets, from which I got the JavaScript idea. For server side use of the API, in which we cannot rely on the JavaScript code to limit the domain, we're using secret keys instead of the public API keys.
api secret is not passed explicitly, secret is used to generate a sign of current request, at the server side, the server generate the sign following the same process, if the two sign matches, then the request is authenticated successfully -- so only the sign is passed through the request, not the secret.
This question has an accepted answer but just to clarify, shared secret authentication works like this:
Client has public key, this can be shared with anyone, doesn't
matter, so you can embed it in javascript. This is used to identify the user on the server.
Server has secret key and this secret MUST be protected. Therefore,
shared key authentication requires that you can protect your secret
key. So a public javascript client that connects directly to another
service is not possible because you need a server middleman to
protect the secret.
Server signs request using some algorithm that includes the secret
key (the secret key is sort of like a salt) and preferably a timestamp then sends the request to the service. The timestamp is to prevent "replay" attacks. A signature of a request is only valid for around n seconds. You can check that on the server by getting the timestamp header that should contain the value of the timestamp that was included in the signature. If that timestamp is expired, the request fails.
The service gets the request which contains not only the signature
but also all the fields that were signed in plain text.
The service then signs the request in the same way using the shared
secret key and compares the signatures.
I will try to answer the the question in it's original context. So question is "Is the secret (API) key safe to be placed with in JavaScript.
In my opinion it is very unsafe as it defeats the purpose of authentication between the systems. Since the key will be exposed to the user, user may retrieve information he/she is not authorized to. Because in a typical rest communication authentication is only based on the API Key.
A solution in my opinion is that the JavaScript call essentially pass the request to an internal server component who is responsible from making a rest call. The internal server component let's say a Servlet will read the API key from a secured source such as permission based file system, insert into the HTTP header and make the external rest call.
I hope this helps.
I supose you mean session key not API key. That problem is inherited from the http protocol and known as Session hijacking. The normal "workaround" is, as on any web site, to change to https.
To run the REST service secure you must enable https, and probably client authentification. But after all, this is beyond the REST idea. REST never talks about security.
What you want to do on the server side is generate an expiring session id that is sent back to the client on login or signup.
The client can then use that session id as a shared secret to sign subsequent requests.
The session id is only passed once and this MUST be over SSL.
See example here
Use a nonce and timestamp when signing the request to prevent session hijacking.

Resources