Saml Implementation Using NodeJS/Passport - node.js

I want to implement saml using passport module of node js using my own idp. But the below links that i found are using some openidp/adfs/shibboleth etc.
https://github.com/bergie/passport-saml
https://github.com/lmarkus/passport-saml-encrypted
https://www.npmjs.com/package/saml2js
I have created two localhost apps ,one functioning as sp(service provider) ,other as idp(identity provider) and while redirecting from sp to idp i am sending the encrypted saml and than i am validating the user using passport ldap module,but i need to know that whether the certificates/public/private key needs to be present at idp side also ? How will idp send back the response and in what format ?

If you are signing the AuthnRequest or encrypting the token then the IDP needs the public keys.
The SP needs the IDP token signing public key.
The IDP will send back an AuthnResponse which contains the SAML token which contains the assertions (claims).
The easiest way to see this is to use the Fiefox SAML tracer add-on.

Related

IdP as the master session

Background
I'm trying implement a browser-based login for a native mobile app from an existing Single Page Application. It uses WebView to render the SPA and it uses Keycloak OIDC as its Identity Provider.
The SPA and IdP is located in completely different domain and authentication is done by redirecting to the SPA domain after a successful login and retrieving the active session (cookie) from IdP domain in one of the SPA's server. The authentication check is achieved by using keycloak middleware which I believe is the protect.js
Summary:
Perform Login -> auth.idp.com
Redirect -> best.app.com
Is Login? -> best.app.com/login
Does auth.idp.com session exists?
User is logged in, redirect -> best.app.com
Token is passed in the URL and is stored only in memory
Token is used to establish WebSocket connection
Issue
Based from the spec, the authorization should happen in the browser / in-app browser, and authorization code must be passed via custom URL scheme. Having that in mind, the SPA that resides in the WebView of native mobile app will never establish a session from IdP's domain since this will be delegated from the browser which is on a different process and obviously using a different cookie store than on WebView in the mobile app, which makes our existing solution to break because it is relying on the IdP's domain cookie.
Proposed Solution
The issue I described above can be mitigated by cutting the reliance on IdP's session and by managing the SPA's own session, which basically means storing the token persistently that can be obtained from the IdP (which the current solution doesn't do).
(I don't want to detail much of the solution since I just want to focus first on the concept of storing the token. I think it's better for me to put this in a separate discussion if someone is interested)
Opinion
It seems like the current implementation doesn't really follow the best practice for OIDC flow but somehow, Keycloak has made some middleware to remove the need to use these tokens (authorization code, id token, and access token)
Relying on IdP's session when implementing SPA or non-web apps seems like not an option, because there is no way to obtain the cookie without reloading the page and provided that IdP session exists in the same cookie store as the SPA.
Redirecting to the IdP's session is not a good user experience for SPA. See the same sentiment here but it seems it does not have any answer: https://lists.jboss.org/pipermail/keycloak-user/2016-October/007937.html
Question
With regards to my proposed solution, i.e., storing the token retrieved from IdP, is there any security flaw or something non-industry standard it's going to introduce? If so, what are those?
Is it typical for OIDC flow to rely on IdP's session (cookie) to check if user is logged in or not?
If answer from #2 is NO, is that authentication flow specific for Keycloak only or does it exists for other IdP as well?
If answer from #2 is YES, is it common for IAM solution to programmatically check if the IdP domain contains a valid session (cookie)?
Is the current implementation flawed knowing we are aiming for SPA?
How does Keycloak handle sessions?
If you're using the default Keycloak middleware in your server and use keycloak.protect() for protecting endpoints, it checks on the request.session['keycloak-token'] which contains the access_token that was created during the token request after user login. If this exist and valid, it means user will not be redirected to Keycloak login page.
How does Keycloak create sessions?
Providing username and password which can be done manually using Keycloak's login page.
Cookies - if you pass valid cookies that are recognized by Keycloak, i.e., KEYCLOAK_SESSION, KEYCLOAK_SESSION_LEGACY, ..., a session will automatically be created.
How to access protected resources?
When using the keycloak-connect client adapters, you can access protected resources if the user agent (browser/app), has a valid session in your server OR if the request contains valid Authorization header.
Standard Solution
Access protected resource via Authorization header and use access_token which the keycloak.protect() also accepts. You can obtain this token in a standard way using Chrome Custom Tabs for Android and ASWebAuthenticationSession for iOS. You can also use AppAuth (iOS, Android) to lessen your work.
Store the refresh_token and access_token from native mobile and inject this in the HTTP request of WebView if possible.
Have a way to check for access_token validity and use refresh_token to request for a new one. If requesting for a new one fails, i.e., the authorization server verifies it's not valid anymore, that means users would need to re login again.
By using the standard solution I have proposed above, you should not need to create a band-aid solution for your issue. Hope this helps anyone that have faced similar issue.

Link another account with oauth on passport

Context:
1 spa
1 api
1 existing passport strategy with jwt
Feature:
Connect additional account with oauth to the existing account
I already have an authentification using passport on nodejs.
This generates a jwt allowing the users to authenticate them with a bearer token in the header of each request to the api.
I would like to add an additional oauth connection to the existing account (actually it is docusign but I think the problem could be the same with other connections).
When there is the redirection with the authorization code, I cannot identify the user which has initiated the connection because I don't have the bearer token in the request header anymore and the data between the two accounts can be different (different emails for example).
Is there a workaround for this or did I miss something?
Thank you for your help
Passport has an Authorize method which can be used to authenticate with a secondary IdP. For example:
Use Passport#Login to enable the user to login to your application's primary IdP
Use Passport#Authorize to enable the user to authenticate with a secondary IdP (such as DocuSign) as needed.
Regarding state for your redirect method (determining which IdP should be used to exchange the authorization code for an access token):
You can use the state parameter that is sent as part of the OAuth authorization code grant flow. But be sure to also use the parameter as a random nonce to guard against a CSRF attack. Eg, send idp1|random_nonce so you can both check the nonce and know that you should communicate with idp1, not idp2.
You can use your application's session machinery to maintain your app's state knowledge of which IdP you're working with at the time.
The only one I can think of is having 2 apps. Each can run on a different port or vd. Each would have their own passport strategy. The two apps can redirect to eachother and/or communicate using API endpoints to pass data between them.

openid connect 2.0 and setting authentication JWT in http only cookie

Since local storage and session storage are both accessible via JavaScript it is best not to store the authentication JWT in either of them to avoid XSS attacks.
Since OpenID connect 2.0 is performed on a separate domain how do we set a server-side HTTP only cookie that contains the authenticated JWT?
My guess is this:
The user goes to your website then clicks sign-in.
The user gets redirected to the 3rd party OpenID connect 2.0 provider.
The user signs in and is now redirected to the route of your choice www.example.com/myredirectlogin.
The user's browser then makes a get request when the redirect lands on my route and it passes in the JWT token in the URI.
The server then validates the JWT via Asymmetric algorithm with the public key given by the provider.
The server then returns a server-side HTTP only cookie with the JWT as the value and the client-side doesn't have any recollection of the JWT since it was only in the URI and isn't stored anywhere else.
My question is: Is the above the correct process to securely handle OpenIDConnect 2.0 flow?
I'm assuming you mean the "Id Token" when you say "authentication JWT", since that's the only JWT required by OpenID connect.
All the flows that OpenID connect supports are listed in the spec. If you want to log in to the authorization server and authenticate to a separate site, then you will often use the "authorization code" flow which doesn't send the ID token to the browser at all. There are other flows defined by OpenID connect, but none of them mention storing the ID token in a cookie - how the session is maintained between the client (the site you're authenticating to) and the browser is a separate issue from authenticating the user.
I found the answer within authorization code flow: https://connect2id.com/learn/openid-connect
List of steps
OAuth 2.0 and OIDC Authorization code flow
The user hits your website's login route
The user is redirected to an identity provider with a proper tenant id
The user is authenticated and is redirected to your callback route with an access token in a query parameter i.e. &access_code=234234sdfkljsak.
a get request is executed on your web server at the callback route with the access token in the query parameters.
this callback get route should then make a post-call to retrieve an actual JWTidentity token from the provider i.e. azure b2c and it will add the access token as part of the request either as a query parameter or post of body.
the provider (Azure B2C) then will respond with an identity JWT token that we will send back to the user's browser as an HTTP-only session cookie that way the user is now SSOed among all browser tabs and the cookie will be sent with every request automatically and is protected from xss.

Why use an idp issued id_token -- versus a custom session cookie?

I'm struggling to understand when to use a session cookie, or just use the IdP issued token as a bearer token.
Is the primary advantage of using an IdP issued token that you get a standards based mechanism for tokens (including ensuring non-tampering, as the JWT is signed)?
As opposed to using a proprietary vendor mechanism of converting it to a claim identity, and then to a custom session cookie (as per owin middleware)?
Doesn't the custom cookie approach have an advantage that one can add non-ipd issued claims (eg: from a system db)?
What other key advantage does using an idp issued id_token have over a cookie based session approach?
Is it maybe that a bearer token approach would not need caching on the server, so that one can re-use it to call 3rd party services that have a different audience value? Whereas the token would be lost if we used a cookie? (I'm reaching, I know, as I don't yet understand how calling 3rd party services works).
The Id token is meant for your user-facing app as proof of who the user is.
It is then up to your app how you choose to authenticate the user on future requests.
While technically you could just store the Id token and use that, as you mentioned, it cannot be extended since it is digitally signed.
Which for most our projects is no good.
At least in regular Azure AD (not sure about B2C), you cannot use the Id token to get an access token for another API.
In there, you would have to get an authorization code in addition to the Id token at the time of sign in,
and exchange that for an access token (which you then cache server-side).
If you use the Id token as an authentication token for your app, keep in mind that Id tokens issued from the token endpoint (when exchanging an authorization code in normal Azure AD) are not signed (I guess since you receive them as a response to your request + over HTTPS).
Only the Id token you get in the redirect back to your app is signed in normal AAD.
I am not sure if this applies to B2C as well.

OpenID Connect - should the id token be sent to the protected resource in this case?

Lets say I have a web API that a native application needs to make requests to. This API needs to authenticate who the user is that's making these requests via the native app. OpenID Connect seems to be the right choice since it is about authentication rather than authorization with OAuth.
The native app sends the users credentials to the IDP and gets back an access token (for authorization) and an id token (for authentication). From what I understand of OIDC, the access token would be sent to the API but the id token is only for the native client app. That doesn't make sense to me because it's the API that cares about who the user is, not the native app.
So why isn't the id token also passed to the protected resource (aka the API)? If you don't pass the id token to the API, what guarantees that the access token is secure and can be used to authenticate the user? Otherwise it would seem to lose the benefit of using OIDC over OAuth.
The OIDC Specification is designed in a way that ID tokens are always for Clients (Native app) and Access tokens are for resources (APIs). The ID tokens are always JWT tokens, but the Access tokens can be of different type.
The purpose of Access token is not to authenticate but to Authorize (Delegated Authorization). If, for some reason, the resource server wanted to know about the user, it can call the user-info endpoint.
The security/validity of token exchange can be validated in several ways:
Using encryption/signature with Public/Private key model where Authorization server encrypts / signs the access token with its private key and resource server decrypts / verifies with the public key.
Using token introspection endpoint to validate the claims, validity of the token etc..
Other attributes like AUD , AZP help in validating the issued access tokens.
Some OIDC providers use ID_Tokens to access API's - this is different to the model suggested by OIDC specification
This article has detailed explanation about these scenarios.

Resources