I have a AWS Cloudfront hosted webpage which takes static pages from S3 and makes calls to custom origin (ALB) for dynamic data. There is OIDC authentication enabled on ALB, so calls to custom origin (my API) passes via rules set at ALB.
In a particular case when my request to custom origin is unauthenticated I am redirected to IdP for login and after successful login I get the cookie in the response header, as this request was sent to IdP from ALB - the issued cookie has domain as ALB DNS. In order for my webpage to use this cookie I have to redirect the call to Cloudfront URL. Now the cookie was issued to ALB which has a different DNS and my Cloudfront URL has a different DNS therefore I am unable to use the cookie.
I tried to catch the cookie value but because it is issued for a different domain i am unable to catch hold, also as a part of design I feel that is wrong. Has some one faced similar type of issue.
AWS ALB OIDC sets session cookie for the same request host domain for which you configure the authentication action in the listener rules.
Also, they set a http only secure cookie, meaning you cannot access it via client side Javascript at all.
Considering this along with your setup, it seems you actually need a (tiny) backend for your web page, so that you can access the response cookie when you make the API call to the mentioned custom origin internally from this backend.
Related
I have a project where I am hosting an ASP.NET MVC site on an AWS EC2 instance behind an AWS application load balancer (ALB).
I have an ALB listener "Authenticate" rule that takes any traffic that does not have a valid cookie and forwards it to the Azure AD OAuth prvoider (aka OIDC Autentication). The authentication works well, and goes as follows:
HTTPS traffic hits my ALB and since there was not a previous OIDC authentication it forwards the user to Microsoft's Azure AD authentication page for my Azure AD tenant.
On successful authentication, Azure AD forward the response to my ALBs response URL. This is where the Azure AD OAuth flow terminates.
The AWS ALB generates it's own OAuth token, which includes my original Azure AD username and claims, signs the JWT with it's own key, and forwards the request to my EC2 instance via a target group, with additional HTTP headers (X-Amzn-*) which include the AWS token info
However, I cannot figure out how to sign the authenticated user out. I have tried
Using Azure ADs own "outh2/v2.0/logout" URL with information about my tenant and client ID (application ID) and then specifying a logout page I have hosted behind my AWS ALB, This only logs out the Azure AD user, but when I return to my site home page the AWS ALB has persisted info via it's own cookie and forwards the traffic without redirecting to Azuure AD with the old AWS JWT (event if the "Exp" timestamp in the JWT has passed). These subsequent requests contain a cookie named "AWSELBAuthSessionCookie-0" whose value is the entire JWT encoded in Base64, but with a cookie expiration of 1 week in the future.
I have tried clearing the "AWSELBAuthSessionCookie-0" cookie both on the server side of my application and also in the client code (by setting expiration to some time in the past) prior to hitting my website home page again, but the "AWSELBAuthSessionCookie-0" is somehow magically restored containing the old, expired JWT
Is there a prescribed way to logout of an AWS ALB-authenticated session that used Azure AD (with a custom tenant, not the public 'common' tenant) as the OIDC provider?
I configured a Session timeout to be as short as 1 second to prevent users from reusing the old cookie.
The AWSELBAuthSessionCookie-0 is a session cookie containing the secure and httponly attributes issued by the load balancer. As this post points out, the cookie must be changed only on the issuing server.
I am using AWS ALB for OIDC authentication, and once authenticated it lands to my backend application where i create a session save it in redis and return the session cookie back to front end.
The front end uses the cookie to commuicate with my backend.
After 10 minutes if the session is expired. Then i want to redirect the page login page.
My question is should i handle the session expiry in my code or does AWS ALB has some intelligence in it to know that my sesion is expired and redirect to logic page.
If i have to explicitly redirect form my code. DO i need to get location header and statu code as 302 as below.
/login is listener in ALB which will authenicate to OIDC
response.getHeaders().setLocation(URI.create("/login"));
response.setStatusCode(HttpStatus.FOUND);
However, when i redirect to /login. which is path route configred in ALB to authenticate via OIDC. I get 404 0r 400 not found from Oauth provider . Is it because of setting location header am not sure
Your session is handled by your backend services and ALB has no knowledge nor access to it. So, you should also handle session expiration in your code.
I have done following configurations
Created loadbalancer
Created user pool
created client with secret and enabled authorization code grant and openid scope
Configured authentication in loadbalancer with above created resource details
Created a listener rule with cognito authentication
Added needed configuration including scope as openid
Configured deny for unauthenticated requests
Problem is when I call the loadbalancer url with the id token or access token(as cookies) that is retrieved from another app client configured to application, The loadbalancer is returning 401
When I try the the configuration authenticate for unauthenticated request. I was able to successfully call the loadbalancer url where I am getting success response(loadbalancer will redirect to cognito login and after successfull login, loadbalancer will set the cookies).
The cookie that being set in the success scenario is being sharded
The cookie entry were like cookie_name-0 and cookie_name-1 (Which I do not know how to reproduce/recreate manually)
The below excerpt from aws docs confirms above sharding behaviour
The load balancer creates the authentication session cookie and sends it to the client so that the client's user agent can send the cookie to the load balancer when making requests. Because most browsers limit a cookie to 4K in size, the load balancer shards a cookie that is greater than 4K in size into multiple cookies. If the total size of the user claims and access token received from the IdP is greater than 11K bytes in size, the load balancer returns an HTTP 500 error to the client and increments the ELBAuthUserClaimsSizeExceeded metric.
How can I successfully call the loadbalancer url with the configuration for unauthenticated request as deny where I need to set the cookie manually?
Manually generated id token/ access token length are around 1k in size, why the loadbalancer retrieved token exceeds 4k in size?
Reference - https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html#authentication-flow
https://aws.amazon.com/premiumsupport/knowledge-center/elb-configure-authentication-alb/
I think what you're trying to do is this: you're getting JWT tokens from cognito, and you want to use them to authenticate a web request through ALB that is using cognito authentication checks. Which is to say you're trying to find some way to generate the AWSELBAuthSessionCookie cookies yourself, or craft a call to /oauth2/idpresponse so that the ALB sets these cookies.
Short answer: You can't as of June 2021.
Here's the answer I got from AWS support on this issue, where I'm trying to get a python client to call through ALB with cognito JWT tokens:
As per the design, automated clients ( such as your python script ) will not be in position to utilize this ALB functionality. Further ALB will not accept any JWT tokens passed by clients in the request. This is to prevent any kind of replay attacks while communicating with load balancer. ALB will only initiate the authentication process if client request triggers authentication rule.
Seems the only way to get those ALB cookies set is by having a web browser open the auth page. Sorry. Cognito sucks. Open standards my ____.
Having trouble setting a cookie (subdomain to parent) in a CloudFront configuration:
S3 bucket serving a static site with a CloudFront distribution. CNAME: example.com
API Gateway API with a custom domain: api.example.com
S3 bucket with a CloudFront distribution with CNAME: files.example.com
The web application on example.com contacts a lambda proxy GET method on api.example.com. This function, among other things, returns headers with values generated by AWS.CloudFront.Signer.getSignedCookies()
{
'Set-Cookie': `CloudFront-Expires=...; Domain=.example.com`,
'SEt-Cookie': `CloudFront-Signature=...; Domain=.example.com`,
'SET-Cookie': `CloudFront-Key-Pair-Id=...; Domain=.example.com`
}
These are being sent to the client with the response, but for some reason are not sent in GET requests to the S3 bucket on files.example.com.
I am not certain if the issue is with the cookies being set or being sent: By looking at the application tab in chrome dev tools it seems that the cookies aren't there. However, in the network tab the cookies are indeed being sent alongside requests to api.example.com (but not with requests to files.example.com).
As far as I can tell the configuration should work in terms of Domain cookie policy (the subdomain is attempting to set a cookie with a parent domain, under which is a different subdomain of the parent, that should receive the cookies). Any other immediate suspects for this sort of behaviour?
You want to use the cookie domain .example.com instead of example.com. The leading . allows subdomains to access the cookie as well.
You also need to ensure that the cookies are being forwarded to the origin in your CloudFront behavior.
I'm using AWS API Gateway with a custom domain. When I try to access https://www.mydomain.com it works perfectly, but when i try http://www.mydomain.com it can't connect.
Is there a way to redirect the http -> https with the custom domain in API Gateway? If not, is there a way to get the http:// links to work just like the https:// links?
API Gateway doesn't directly support http without TLS, presumably as a security feature, as well as for some practical considerations.
There is not a particularly good way to do this for APIs in general, because redirection of a POST request from HTTP to HTTPS is actually a little bit pointless -- the data is has already been sent insecurely by the time the redirect is generated, unless the client has asked the server to inspect the request headers before the body is sent, with Expect: 100-continue.
You can create a CloudFront distribution, and configure it to redirect GET and HEAD requests from HTTP to HTTPS... but if you send a POST request to such a distribution, CloudFront doesn't redirect -- it just throws an error, since (as noted) such a redirection would be more harmful than helpful.
However... if GET is your application, then it's pretty straightforward: first, deploy your API with a Regional (not Edge-Optimized) API endpoint with a system-assigned hostname, not a custom domain.
Then, create a CloudFront distribution that uses this regional API endpoint as its origin server, and configure the CloudFront distribution's behavior to redirect HTTP to HTTPS. Associate your custom domain name with the CloudFront distribution, rather than with API Gateway directly.