I am setting up a single sign on SAML service with zendesk. I am writing a custom SAML server using node/express and using node-samlp.
Zendesk is the SP,
My SAML server has its own IdP
The user steps are as follows:
Navigates to account.zendesk.com and gets redirected to account.com/login?SAMLRequest=asdfasdfafsd
User then enters credentials and posts to the node server.
At the server I am able to parse* the SAMLRequest, verify the user and give the user a SAMLResponse.
The user receives from the node server a SAMLResponse and is redirected to: account.zendesk.com?SAMLResponse=asdfasdf&RelayState=xxx
At that point the user gets a page not found. I am not sure what I'm doing wrong, at this point I'm guessing that my SAMLResponse is either badly formatted or I am redirecting the user to the wrong address.
PS: initially samlp didn't work right out of the box, I forked the repo and updated a couple dependencies and it started to work.
*I was unable to parse the SAMLRequest from zendesk initially. When I used decodeURIComponent on the query param, there were new line characters and white spaces which I replaced with a '+' which made it work. Then I realized that their query param seems to not be URI encoded...
For one thing there is no such thing as a SAML Redirect Binding for the SAML Response. You can send the SAML Response as a form-encoded parameter in an HTTP POST, but you cannot pass it as a query parameter in a redirect. Your SAML IDP implementation is not spec-compliant and Zendesk may be rejecting the message because of that.
See http://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf#page=16 :
Identity Provider issues to Service Provider In step 5, the identity provider issues a message to be delivered by
the user agent to the service provider. Either the HTTP POST, or HTTP
Artifact binding can be used to transfer the message to the service
provider through the user agent. The message may indicate an error, or
will include (at least) an authentication assertion. The HTTP Redirect
binding MUST NOT be used, as the response will typically exceed the
URL length permitted by most user agents.
Indeed, as #hans-z already mentioned, a SAMLResponse is always sent over POST! You can trigger this browser POST by serving a page that automatically submits a form (containing the SAMLResponse) on page load (through JavaScript).
Since you're sending over POST, your SAMLResponse should not be URL encoded anymore.
Related
My goal is to tell if a user is authenticated, and get their name and email. So far, I can only do the first.
My server app is a NodeJS/Express router. The OIDC server is provided by my company, I believe is compliant, but not under my control. I can do an "Authorization code flow": response_type="code". My Node app eventually responds to the callback route and I can get the "code" and "grant_id" query string values. Other than knowing they exist and presuming that means the user is authorized, I have no idea what I can do with those values. It appears that the only scope that works is "openid".
If I can get the access_code, I can call the UserInfo service and get these values.
My other choice is to do an implicit call. Unfortuantely, the OIDC service it provides the "access_code" and other values after a hash mark on the callback. I believe the flow to be like this:
User makes call to Node app. Detects a lack of authentication, issues redirect to SSO service implicit authorization
User's browser follows redirect to SSO service implicit authorization. User fills it out and is successfully authenticated.
W3 returns a redirect to the provided callback URL.
User needs to cooperate with the app, somehow parse the query string parameters to get the access token and pass this back to the Node application.
The browser calls the provided Node callback application, but without the necessary access token.
I think I could make a proxy server to force OIDC to give my node server the request, just so I can get the access_token. It seems like a very convoluted way to do this, so I have to think there's some simpler way.
Again, all I want is to know the user is authorized to use the app, and what their name and email is. It should not be this hard.
You can use the ID-token to get the details about the user.
It is also important that your identity provider is all about authentication. Final authorization checks should be done in the client, by examining the scopes/claims in the token.
I have deployed an azure app to azure app service. It has no frontend part, simply an API app that does some http calls.
I have configured the Authentication/Authorization section to log anonymous requests using Azure AD.
I have registered the app in AD and connected everything together.
Here is the problem:
Upon first sign in, everything works great, I sign in and the jwt token is sent to my app and I can parse it.
However if I am already signed in and go to my app again a jwt token is not sent. However doing a GET request to /.auth/me simply returns [].
If I send a request to /.auth/refresh I get a 403 forbidden.
A bit stuck on how to proceed here. Thanks.
What is the status of your Action to take when request is not authenticated? Is it Allow anonymous or Sign-in with Azure AD?
If it is the former (allow anonymous), what you observe is expected behavior.
If it the latter - it is not expected.
When you set to "Allow anonymous" you instruct to not do anything if request is not authenticated. So, nothing happens, and you do not see any token. If you configure to "Login with ...", you will be automatically redirected if you are not authenticated.
UPDATE
Can you define "If I am already signed in.."? Also, if you configured to not allow anonymous request, every request hitting your application code will be authenticated. The app service authentication module takes care of intercepting unauthenticated calls and redirects them. Additionally /.auth/me endpoint will redirect to sign-in all unauthenticated calls, but /.auth/refresh will not.
You may also want to enable token store, which is enabled by default. The option is located directly under the list with authentication providers.
You can find more information about authentication and authorization in Azure App Service here:
https://learn.microsoft.com/en-us/azure/app-service/app-service-authentication-how-to - check out the sections access user claims, retrieve tokens in app code and refresh identity provider token
I've been scratching my head for about 2 days on how to solve what seemed to be a simple task, but it's starting to drive me crazy.
I have an application where users will use SAML 2.0 to authenticate.
I have a react-application set up for the front-end, and was thinking I was gonna use JWT to secure the rest-api communication between front-end and backend.
When a user signs in, the flow is the following:
User accesses www.server.com/ and gets served the static HTML with react-application
User clicks 'Sign in' and accesses www.server.com/login
passport-saml redirects user to saml identity provider. User logs in.
User calls back to www.server.com/callback with a SamlResponse in the req.body which is decoded by passport-saml and put into req.user.
If the user doesn't already exist, I create the user in the database.
I create a JWT.
What should I do next? The problem is that the user is not in the react-application when calling back from the identity provider, so I've lost all state in the application, so whatever I reply with will get sent to the browser.
Is there any way I could force the browser to give me the SamlResponse which the identityprovider is calling back with? Then I could send it to the server as a http-request from the react-application.
After some thinking, I came up with the following solution which worked quite nicely for me.
SAML has something called RelayState which is a property that the Service Provider has to respond with. So now the process looks like this:
User accesses http://frontendserver.com and gets server the static page with the React application (not signed in.).
User clicks 'Login' and gets redirected to http://backendserver.com/login/?RelayState=http://frontendserver.com which authenticates via passport-saml and redirects user to SP. So I pass the origin of the request in RelayState.
User calls back to http://backendserver.com/callback with the SamlResponse, which includes the RelayState.
I create a token, and redirect the user to RelayState/#token.
I can then parse the url in the React application, and add the token as a header for any further requests.
This might've seemed like the obvious way to do it, but it took me quite a while to figure out that this would work.
I know this question is for Node backend, but I found an article of the implementation for a PHP/Apache webserver backend here and I think it can help someone trying to understand the flow of the process of how this type of thing works.
I am trying to implement SAML authentication through nodejs. We got issuer id and assertion url (assume abc.com) from the third party SAML maintenance team.
I have tried both the following two libraries.
https://github.com/lmarkus/passport-saml-encrypted
https://www.npmjs.com/package/saml2-js#note_options
I am facing following issue. Assume i am currently in the url
http://127.0.0.1:58374/index.html#/home. Now if click login button, it is going to the identity Url. After giving the user credentials, it validates and directly redirecting to different url abc.com which is given by third-party maintenance team. Whatever callback url, i am giving at backend it didn't use.
My problem is after providing the user credentials, control is not coming back to backend node server where i can fetch SAML response. Just identity portal directly redirects to the given url. So not able to get the SAML response data.
How do i can recieve the SAML response after login validation before re-directing. Kindly share your thoughts.
thanks
r karthik.
I am using .htaccess authentication for my website. Once the authentication happen with AD vi LDAP, how the authenticated user's session and cookies got created in the browser and where the redirection is really happening to my website. Please help in this or please share any links which explains this process.
Take a look at the wiki pages for BASIC authentication and DIGEST authentication, both is an authentication mechanism that is on the protocol (HTTP). Basically, this is what the browser sees (heavily paraphrasing here):
Attempt to request a URI, webserver replies with a 401 response and a WWW-Authenticate header
This header contains a REALM, kind of like a group of pages that all belong to the same authentication realm
Browser pops up a modal dialog box asking for a username and password.
Depending on BASIC or DIGEST, that password is sent back to the webserver along with the original request that resulted in a 401.
If the credentials are incorrect, a 403 is returned and the browser shows an appropriate error message. Otherwise, the webserver returns the requested page.
The browser knows this URL belongs to the REALM, so every time this URL is requested, authorization is automatically sent along with the request.
If the browser requests something else that also requires authentication, and it belongs to the same REALM, the browser will automatically send the authorization when the webserver returns a 401 and the REALM.
How the webserver is configured with REALM info and a list of credentials varies. It could be hooked up to a LDAP database, Active directory, a flat file with hashed passwords, etc.
This is different than cookies or other webapp-level authentication because it doesn't use the HTTP protocol to authenticate. The webapp has a page with forms for a username and password, those are sent via a POST/GET request as parameters, and it's up to the webapp to determine if those credentials are valid. If they are valid, the webapp could choose to store a session ID as a cookie so the browser can continue to browse pages served by the webapp.
One of the end-user differences between this and HTTP authentication is that the webapp can delete the cookie, or invalidate that session, essentially logging the user out of the webapp. This isn't really possible with HTTP authentication because the browser blindly submits an authorization header when requesting pages under the same REALM. One way to get around this is to make the webserver forcefully return a 403 when a protected page is requested.
Another difference is that with HTTP authentication, you can include a username/password as part of the URL. How does http://user:pass#host.com authentication work?
Also see: http://blog.distributedmatter.net/post/2008/06/09/HTTP-authentication-mechanisms-and-how-they-could-work-in-Restlet for a more complete description of authentication in general.