Advanced Authentication Workflows with OpenID Connect using Gloo API Gateway – Part 2
In the previous Blog post, we covered how Gloo can be used to authenticate users with OIDC and how it can process the JWT token returned by the identity provider.
The JWT token was stored as a cookie in the web browser and sent with each HTTP request as a header.
We configured Gloo to manipulate each request as follow:
- extract the token from the cookie header
- validate the token
- extract claims from the token
- add new headers based on the claims (for example, a header with the email of the user)
- apply RBAC rules to determine if the request should be authorized (based on the email, method, path, …)
This workflow works well, but it has both pros and cons:
- pro: the cookie is sent with each HTTP request (because it’s stored as a cookie), so we can do all these nice manipulations.
- con: even if the cookie is stored with the attributes
Secure
andHttpOnly
, some users prefer to avoid any sensitive information in the web browser. - con: in some cases, there are many claims in the token which can cause the size of the cookie to exceed the 4 KB allowed by the web browers.
Caching the JWT token in Redis
To avoid storing the cookie in the web browser, you can enable caching when creating the Gloo AuthConfig
:
apiVersion: enterprise.gloo.solo.io/v1 kind: AuthConfig metadata: name: google-oidc namespace: gloo-system spec: configs: - oauth2: oidcAuthorizationCode: appUrl: https://mydomain.com callbackPath: /callback clientId: $CLIENT_ID clientSecretRef: name: google namespace: gloo-system issuerUrl: https://accounts.google.com scopes: - email session: failOnFetchFailure: true redis: cookieName: session options: host: redis.gloo-system.svc.cluster.local:6379
As you can see, the token will be stored in Redis.
Let’s start with a simple VirtualService
:
apiVersion: gateway.solo.io/v1 kind: VirtualService metadata: name: default namespace: gloo-system spec: sslConfig: secretRef: name: upstream-tls namespace: gloo-system virtualHost: domains: - '*' routes: - matchers: - prefix: / routeAction: single: upstream: name: default-httpbin-8000 namespace: gloo-system options: extauth: configRef: name: google-oidc namespace: gloo-system
If you try to access the application using your browser, here is the output you should get:
{ "args": {}, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "fr-fr", "Content-Length": "0", "Cookie": "session=6DDMJFK2P35EQ42LZFUSBBHTZ3Y5X6CWKNJ33HHDSN2PW3HFNRGYN47UKM5BOE362VGSMW4VCMK7ZNOK7D45EXIKKDG3HGJOWXL7SWQ=", "Host": "mydomain.com", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", "X-Envoy-Expected-Rq-Timeout-Ms": "15000", "X-User-Id": "https://accounts.google.com;117577190223680183681" }, "origin": "192.168.149.8", "url": "https://mydomain.com/get" }
The web browser is still sending a cookie with each request, but this cookie doesn’t contain the token anymore.
It a session cookie that allows Gloo to retrieve the JWT token from Redis.
Everything works well, but we can’t access the token anymore and perform all the nice manipulations we were able to perform before. So, how will the backend application able to determine who the user is if it needs this information ?
Forward the JWT token upstream
Gloo provides an option to create a new header with this token when it retrieves it from Redis.
Let’s update the AuthConfig
to enable this option:
apiVersion: enterprise.gloo.solo.io/v1 kind: AuthConfig metadata: name: google-oidc namespace: gloo-system spec: configs: - oauth2: oidcAuthorizationCode: appUrl: https://mydomain.com callbackPath: /callback clientId: $CLIENT_ID clientSecretRef: name: google namespace: gloo-system issuerUrl: https://accounts.google.com scopes: - email session: failOnFetchFailure: true redis: cookieName: session options: host: redis.gloo-system.svc.cluster.local:6379 headers: id_token_header: "jwt"
Here is the output you should get if you refresh the web page:
{ "args": {}, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "fr-fr", "Content-Length": "0", "Cookie": "session=6DDMJFK2P35EQ42LZFUSBBHTZ3Y5X6CWKNJ33HHDSN2PW3HFNRGYN47UKM5BOE362VGSMW4VCMK7ZNOK7D45EXIKKDG3HGJOWXL7SWQ=", "Host": "mydomain.com", "Jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImYwOTJiNjEyZTliNjQ0N2RlYjEwNjg1YmI4ZmZhOGFlNjJmNmFhOTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI2MzUzODgzNDA1MjEtMHNtNzFtZ29rZXFmbGs0ajlyZGJncHQyNnRkbGJybjMuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI2MzUzODgzNDA1MjEtMHNtNzFtZ29rZXFmbGs0ajlyZGJncHQyNnRkbGJybjMuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTc1NzcxOTAyMjM2ODAxODM2ODEiLCJlbWFpbCI6ImRqYW5ub3RAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJOWHVKNWpKZnhkTGVhTHFDLUtPMF9nIiwiaWF0IjoxNjA0NTY1MDg2LCJleHAiOjE2MDQ1Njg2ODZ9.wFrDI_pXNgOzOWKEt0ou0MNfsWUbVWP4KtnkyuqET38wia6c3gX4g5Ck2GPHBy1ZuKC9luc1SK3n11G9VvhmiR94ysk6kWJDtK1uEYv6MNt4KLFtiO9iQ0Mx8yiqXgKPFdGEssqLbE7ifQtko5YJA8qARI6OwPxHt3FwjHYI8G_qJmiTqcJGwXi5yPj47rXuwUc7akEwFga5w4EP46Xsv7onxnoMbeM8w4zJNncVzm9qruf56aFGm9vWgCl3cdIV6fDZfFZkBrgt4r4S-UADfrqqogvSe9RajXxkiln_TKtlrVXha_--pfvrhgf4uqyY4sfwTj2qrjHsiDUNbDbBMQ", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", "X-Envoy-Expected-Rq-Timeout-Ms": "15000", "X-User-Id": "https://accounts.google.com;117577190223680183681" }, "origin": "192.168.149.8", "url": "https://mydomain.com/get" }
Great ! Now you have your JWT token in a header and you can perform all the manipulations like in the previous Blog post.
Update your VirtualService
as follow:
apiVersion: gateway.solo.io/v1 kind: VirtualService metadata: name: default namespace: gloo-system spec: sslConfig: secretRef: name: upstream-tls namespace: gloo-system virtualHost: domains: - '*' routes: - matchers: - prefix: / routeAction: single: upstream: name: default-httpbin-8000 namespace: gloo-system options: extauth: configRef: name: google-oidc namespace: gloo-system headerManipulation: requestHeadersToRemove: - "cookie" jwt: providers: google: issuer: https://accounts.google.com tokenSource: headers: - header: Jwt claimsToHeaders: - claim: email header: x-solo-claim-email - claim: email_verified header: x-solo-claim-email-verified jwks: remote: url: https://www.googleapis.com/oauth2/v3/certs upstreamRef: name: google-jkws namespace: gloo-system rbac: policies: viewer: permissions: methods: - GET pathPrefix: /get principals: - jwtPrincipal: claims: email: djannot@gmail.com
Here is the output you should get if you refresh the web page:
{ "args": {}, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "fr-fr", "Content-Length": "0", "Host": "mydomain.com", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", "X-Envoy-Expected-Rq-Timeout-Ms": "15000", "X-Solo-Claim-Email": "djannot@gmail.com", "X-Solo-Claim-Email-Verified": "true", "X-User-Id": "https://accounts.google.com;117577190223680183681" }, "origin": "192.168.149.8", "url": "https://mydomain.com/get" }
What happens here ?
Gloo gets the token from the Jwt
header, validates it, extracts some claims from it and add headers for these claims. Finally, it applies RBAC rules based on the claims.
So, we now have the best of both worlds: the JWT token isn’t stored in the web browser anymore, but we can perform all the manipulations we want from it.
Logout
Gloo also supports specifying a logout url. When specified, accessing this url will trigger a deletion of the user session.
You can enable this option by updating the AuthConfig
as follow:
apiVersion: enterprise.gloo.solo.io/v1 kind: AuthConfig metadata: name: google-oidc namespace: gloo-system spec: configs: - oauth2: oidcAuthorizationCode: appUrl: https://mydomain.com callbackPath: /callback clientId: $CLIENT_ID clientSecretRef: name: google namespace: gloo-system issuerUrl: https://accounts.google.com scopes: - email session: failOnFetchFailure: true redis: cookieName: session options: host: redis.gloo-system.svc.cluster.local:6379 headers: id_token_header: "x-token" logoutPath: /logout
Get Started with Gloo
Gloo is available in open source and enterprise editions addressing a wide range of edge and API gateway use cases. Learn more about Gloo by visiting the additional resources below.
- About Gloo API Gateway
- Request an enterprise trial
- Register for an upcoming event
- Join the conversation in the community slack