How should I access requested host in a Lagom service? - scala

I have a Lagom ServerServiceCall, which gives me access to Lagom's RequestHeader containing information about the requested URI (path) and the message protocol (media type).
What I'd really like, however, is to find the 'host' and protocol which the client requested. In a standard Play application it looks like this data is in the 'host' and 'proto' fields of the RequestHeader, but the Lagom version of RequestHeader doesn't contain those fields. I can go poking around in the header values themselves for Host and / or various X-Forwarded-... headers but perhaps there's a better way?

Lagom will let you read the headers from a request like you already described but the actual header where the information you want is located will depend on the API Gateway you deploy.
For instance, in developer mode Lagom starts an API Gateway that currently doesn't add X-Forwarded-... info, but when you deploy in production your HAProxy gateway could keep that information.
Lagom doesn't set any convention or restriction.

Related

Spring boot oauth2 integration with keycloak using Spring webflux along with multi-tenancy

I need to implement Authentication & Authorization using spring boot oauth2 with keycloak as a provider.
I also need to support muti-tenancy. I tried example with authentication using spring-boot-starter-auth2-client to authenticate, but not able to add multi-tenancy.
When I used spring-boot-starter-auth2-client, I need to configure hardcode keycloak urls(specific to one tenant) in properties and not able to support multi-tenancy.
I also analyze spring-boot-starter-auth2-resouce-server, but not clear. I understand that resouce server use for validation of token and expiry.
Note: I don't want to use keycloak adapter library which is provided by keycloak.
Could you please help me -
Where need to use spring-boot-starter-oauth2-client and spring-boot-starter-oauth2-resouce-server?
Is spring-boot-starter-oauth2-resouce-server also use to authentication?
How to authenticat user using spring-boot-starter-oauth2-client and pass to spring-boot-starter-oauth2-resouce-server for authorization.
How to implement multi-tenacy e.g. take tenant id from url and redirect user to tenant specific keycloak login page.
I tried some example but won't succeed, working example will be helpful with -
Spring Webflux + spring-boot-starter-oauth2-client+ spring-boot-starter-oauth2-resouce-server + multi-tenancy + keycloak as a provider.
Thanks & Regards,
Pravin Nawale
tried some example found on internet, but didn't work.
This question should not be answered because:
it is actually a container for many questions
quite a few are way too wide or lack precision.
But as it seems to be a first question... (break it down next time, give more details and edit your question when you get comments asking precisions)
1. Where need to use spring-boot-starter-oauth2-client and spring-boot-starter-oauth2-resouce-server?
This one is important to start with as I suspect you lack OAuth2 background, specifically regarding involved parties and how it is implemented with spring-security:
spring-boot-starter-oauth2-client is to be used with OAuth2 clients:
apps serving UI with oauth2Login (#Controllers with methods returning template names)
apps consuming REST APIs with auto-configured Spring client: WebClient, #FeignClient, RestTemplate
spring-boot-starter-oauth2-resouce-server is to be used with resource-servers: apps serving REST APIs (#RestController or #Controller with #ResponseBody)
Now, if your app has controllers for both the resources and the UI to manipulate it (with Thymeleaf or any other server-side rendering engine), then define two different security filter-chains: one for each, ordered, and with securityMatcher in the first in order to limit the routes it applies to (the second being used as fallback for unmatched routes). Sample in this answer (the sample is for servlet, but it's the exact same principles): Use Keycloak Spring Adapter with Spring Boot 3
2. Is spring-boot-starter-oauth2-resouce-server also use to authentication?
OAuth2 requests should be authorized with an Authorization header containing a Bearer access-token.
The client is responsible for acquiring such an access-token from the authorization-server before sending requests to resource-server.
Your question is not quite clear but here are a few statements which could answer:
resource-server should return 401 (unauthorized) and not 302 (redirect to login) when authorization is missing or invalid => do not configure oauth2Login in resource-server filter-chain. Again, this is client business
resource-server is responsible for resources access-control: check that access-token is valid, that the user has required authorities, etc.
3. How to authenticat user using spring-boot-starter-oauth2-client and pass to spring-boot-starter-oauth2-resouce-server for authorization.
This question is not focused enough to get a single answer: what kind of client? what kind of request? context?
I see three main cases here:
the UI is rendered on Spring server with Thymeleaf, JSF, and alike => use spring's oauth2Login and refer to its documentation to overrides defaults and implement your authorization-server selection logic
the UI is rendered in the browser (Angular, React, Vue, ...) and you are ok to make it an OAuth2 client => find a certified client lib for your framework and implement the logic in the client (angular-auth-oidc-client, for instance, supports multi-tenancy)
the UI is rendered in the browser, but you prefer to implement the Backend For Frontend pattern to hide tokens from browser, then choose a BFF (like spring-cloud-gateway with tokenRelay filter) and refer to its doc for implementing your logic in it
If that can be of any help, I have:
here a tutorial for configuring an app with a Thymeleaf UI client and REST API
there a sample repo with an Angular workspace (app configured as OIDC client + API client lib generated from OpenAPI spec) and spring-boot resource-server (using servlet, but this makes no difference to the client).
4. How to implement multi-tenacy e.g. take tenant id from url and redirect user to tenant specific keycloak login page
Note
One of key principles of OAuth2 is that identities (tokens) are emitted (issued) by trusted 3rd parties (authorization-servers) => you must configure the list of issuers your resource-servers can trust (and clients can fetch tokens from). This list is static (loaded with conf at startup).
Accept identities from various issuers on the resource-server
This is done by overriding the default ReactiveAuthenticationManagerResolver<ServerWebExchange> in your SecurityWebFilterChain configuration: http.oauth2ResourceServer().authenticationManagerResolver(authenticationManagerResolver)
I provide with thin wrappers around spring-boot-starter-oauth2-resource-server which support multi-tenancy just by defining properties. Complete sample there:
Instead of spring-boot-starter-oauth2-resource-server (which is a transient dependency):
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-jwt-resource-server</artifactId>
</dependency>
Instead of all your resource-server Java conf (unless you want access control from configuration and not with method-security, in which case, you'd have to define an AuthorizeExchangeSpecPostProcessor bean here). Of course, you'll have to add here a client filter-chain with a restrictive securityMatcher if you also serve UI client with oauth2Login:
#EnableReactiveMethodSecurity
#Configuration
public class SecurityConfig {
}
Instead of spring.security.oauth2.resourceserver properties:
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/realm-1
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.client-1.roles,resource_access.client-2.roles
com.c4-soft.springaddons.security.issuers[1].location=https://localhost:8443/realms/realm-2
com.c4-soft.springaddons.security.issuers[1].authorities.claims=realm_access.roles,resource_access.client-1.roles,resource_access.client-2.roles
# Comma separated list of routes accessible to anonymous
com.c4-soft.springaddons.security.permit-all=/api/v1/public/**,/actuator/health/readiness,/actuator/health/liveness
# Fine-grained CORS configuration can be set per path as follow:
com.c4-soft.springaddons.security.cors[0].path=/api/**
com.c4-soft.springaddons.security.cors[0].allowed-origins=https://localhost,https://localhost:8100,https://localhost:4200
# this are defaults and can be omitted
com.c4-soft.springaddons.security.cors[0].allowedOrigins=*
com.c4-soft.springaddons.security.cors[0].allowedMethods=*
com.c4-soft.springaddons.security.cors[0].allowedHeaders=*
com.c4-soft.springaddons.security.cors[0].exposedHeaders=*
If you don't want to use "my" wrappers, just copy from the source, it is open.
Redirect the user to the right authorization-server from client UI
As explained at point 3., this depends on the kind of client, used framework and if BFF pattern is applied or not
5. I tried some example but won't succeed, working example will be helpful with - Spring Webflux + spring-boot-starter-oauth2-client + spring-boot-starter-oauth2-resouce-server + multi-tenancy + keycloak as a provider
With all the elements above and linked resources, you should have enough to find your own path

Editing the Metadata of HttpRequest for Akka GRPC Authentication

Akka-grpc documentation provides a basic example of adding authentication through Akka-http.
At the end, it suggests that the authentication result can be embedded in the metadata of the HttpRequest and can be accessible in the grpc service methods as the metadata argument, with proper configuration enabling the powerAPI, in the conf file.
Also, on the details page, it says that metadata is for providing access to request headers in the grpc service interfaces; which makes sense. It also provides an example of how a Metadata instance can be built with MetadataBuilder.
Now, the question is this:
How can I pass the constructed metadata object with information on the authenticated user to the HttpRequest? Am I supposed to use the addHeader method or another one?

URL design for a proxy API

We have multiple API's running for an enterprise. As per our limitation client will allow only one static IP to receive all Inbound/Outbound requests.
So, we need to expose a single API as a bridge between the client system and API's running behind.
How to approach this design?
How to design the URL for this proxy API?
What edge functions does this API need to provide?
Any help would be highly appreciated. Thanks!
You do not need to use web services consumer, yet will need to create a POC.
Define A RAML with required path and RAML, scaffolding should give you API took kit, and connect an HTTP request
use HTTP request
Examples:
Define Headers --- attributes.headers.id etc
queryParams ---- attributes.queryParams.date
if you are sending JSON payload across from ex: postman, change Mime type to application/json
sample http properties for request
http.host=myHost
http.port=8872
http.base.path=/myproxy/services

API naming conventions when using both HTTP and WebSockets

What is the most common approach to URI naming for API which uses both HTTP and WebSocket protocols for similar requests? Are there any common conventions for that?
Lets suppose we have a HTTP request returning the collection of users:
localhost/users
This request should return a list of registered users.
On similar WS request, the server should open WebSocket channel and send a list of users to the client over it every time the list is updated (e.g. user was added or removed, etc).
How should the URI for WebSocket request look like?
I see several options:
It may be the same, localhost/users. The difference should be only in the request headers (Upgrade: WebSocket). The drawback is that it can be confusing as requests with same URI return different responses dependent on provided headers
localhost/users-ws. This seems a little bit ugly for me as the API grows each time we have similar WS request for a HTTP request
localhost/users/ws. This breaks the possibility of extending the URI with variable, for example we can't use localhost/users/{id} here no more.
Store all WS requests under common ws domain - localhost/ws/users. This is also ugly as we break the order of domains in the URI and can't redirect requests with users domain to the specific handler
So at the monent I don't see an option without obvious drawbacks:)
If someone can provide any examples of big projects like StackOverflow or GitHub where WS and HTTP are used together, it will be very helpful.
I am not aware of any common naming conventions for WebSockets but for web APIs there is a naming convention to start the route with /api/, example api.example.com/api/users.
Following that practice, it could be argued that example.com/ws/users could be a fitting route name to use.

What is best way to pass pagination info to RestController?

Almost each major cloud storage service provider like Google, Box, OneDrive use request parameters to convey pagination info to web services and update response object to further communicate next page info to the client.
Despite of having standard HTTP Headers like Range and Link and custom headers people tend to use query parameters to convey such info.
Using such standard headers to convey pagination info between client and server keeps the standard response unique as we don't need to pass such info via response attributes.
So, what is the best practice to implement pagination in REST APIs? With HTTP Headers or with Query Parameters???
According to the http specs:
A proxy MAY discard a Range
header field that contains a range unit it does not understand. That means any intermediate proxy, including caching servers, may throw out your `Range header before it gets to your server.
Link headers have nothing to do with pagination.
A proxy MUST forward unrecognized header fields unless [..] the proxy is specifically configured to block, or otherwise transform, such fields. I read that as a proxy can be configured to block all unrecognized headers, which is the same problem as for Range headers.
Beyond that, using query parameters for pagination (preferably offset/limit) is a standard. If you have a good reason to buck the standard, that's fine. The downside is now everybody who is using your API has to learn your way of doing things in addition to the way everybody else does things. This lowers adoption and increases support calls.