Skip to main content

Authentication

Client configuration

GeoGirafe supports 2 types of authentication:

1. The legacy authentication

When working with c2cgeportal, the legacy workflow authentication can be used. In this case, the section gmfAuth should be configured in the config.json file.
See https://doc.geomapfish.dev/docs/configuration#gmfauth.

More information about the authentication in c2cgeoportal : https://camptocamp.github.io/c2cgeoportal/master/integrator/authentication.html

2. The OpenID-Connect authentication

When working with an external Identity-Provider like for example Keyckloak or Zitadel, a modern OIDC workflow can be used. In this case, the section oAuth should be configured in the config.json file.

More about OpenId-Connect: https://openid.net/developers/how-connect-works/

Backend configuration

If the GeoGirafe client is not running on the same domain as the backend, the backend needs to be configured.
This section only describes the configuration of the backend c2cgeoportal. For other backends, please contact the backend provider.

In the file env.default or env.project, the variable AUTHTKT_SAMESITE has to be set to None, to allow authentication cookies to be sent to the backend from another domain.

AUTHTKT_SAMESITE=None

2. Configure allowed referrers

The frontend domain has to be allowed as referrer in the vars.yaml file:

authorized_referers:
- "https://app.localhost:8080/"
- "https://demo.geomapfish.dev/"

3. Configure the CORS

There are 2 possibilities to configure the CORS:

  • Do the configuration at the backend level, using the integrated configuration of c2cgeoportal.
  • Do the configuration at the haproxy level, before the backend is accessed.

3.1 At the c2cgeoportal level

With this option, you can fine-tune which routes accept authentication, and which do not. However, for some routes, authentication may not be configurable. Please refer to the c2cgeoportal documentation : https://camptocamp.github.io/c2cgeoportal/master/integrator/headers.html if you need more information.

1. To manage CORS within your geoportal, the headers needs to be configured in the vars.yaml file:
# Control the HTTP headers
headers:
dynamic: &header {}
index: *header
api: *header
apihelp: *header
profile: *header
raster: *header
vector_tiles: *header
error: *header
themes: &auth_header
cache_control_max_age: 600
cache_control_max_age_nocache: 10
access_control_max_age: 600
access_control_allow_origin:
- "{VISIBLE_WEB_PROTOCOL}://{VISIBLE_WEB_HOST}"
- "https://app.localhost:8080"
- "https://demo.geomapfish.dev"
headers:
Strict-Transport-Security: max-age=31536000; includeSubDomains
config: *auth_header
print: *auth_header
fulltextsearch: {}
mapserver: *auth_header
tinyows: *auth_header
layers: *auth_header
shortener: *auth_header
login: *auth_header
2. Those vars need to be updated. At the bottom of the vars.yaml file, add the update_paths to the existing list:
update_paths:
- headers.dynamic.headers
- headers.index.headers
- headers.api.headers
- headers.apihelp.headers
- headers.profile.headers
- headers.raster.headers
- headers.vector_tiles.headers
- headers.error.headers
- headers.themes.headers
- headers.config.headers
- headers.print.headers
- headers.fulltextsearch.headers
- headers.mapserver.headers
- headers.tinyows.headers
- headers.layers.headers
- headers.shortener.headers
- headers.login.headers
3. Control the webserver configuration

If c2cgeoportal is served by a WebServer like apache for example, the CORS-Headers might be overridden by the WebServer configuration. Ensure that the CORS headers are not overridden or restricted by the WebServer configuration. Make sure that the appropriate headers, such as Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers, are correctly set and not blocked or altered. This ensures that cross-origin requests function as expected when interacting with c2cgeoportal from different domains.

3.2 At the haproxy level

With this option, you can accept authentication for all routes, including for example static images. However, configuration granularity will be lower.

An LUA script will be used at the haproxy level, to automatically add the right CORS infos to all response:

1. If it does not exist yet, create a directory haproxy at the same level as geoportal in you backend:
mkdir haproxy
2. Create 2 files in this directory:

File haproxy/cors.lua:

--
-- Cross-origin Request Sharing (CORS) implementation for HAProxy Lua host
--
-- CORS RFC:
-- https://www.w3.org/TR/cors/
--
-- Copyright (c) 2019. Nick Ramirez <nramirez@haproxy.com>
-- Copyright (c) 2019. HAProxy Technologies, LLC.

-- Loops through array to find the given string.
-- items: array of strings
-- test_str: string to search for
function contains(items, test_str)
for _,item in pairs(items) do
if item == test_str then
return true
end
end

return false
end

-- When invoked during a request, captures the Origin header if present
-- and stores it in a private variable.
function cors_request(txn)
local headers = txn.http:req_get_headers()
local origin = headers["origin"]

if origin ~= nil then
core.Debug("CORS: Got 'Origin' header: " .. headers["origin"][0])
txn:set_priv(headers["origin"][0])
end
end

-- Add headers for CORS preflight request
function preflight_request(txn, method, allowed_methods)
if method == "OPTIONS" then
core.Debug("CORS: preflight request OPTIONS")
txn.http:res_add_header("Access-Control-Allow-Methods", allowed_methods)
txn.http:res_add_header("Access-Control-Max-Age", 3600)
txn.http:res_add_header("Access-Control-Allow-Headers", "Authorization, X-Requested-With, Content-Type")
end
end

-- When invoked during a response, sets CORS headers so that the browser
-- can read the response from permitted domains.
-- txn: The current transaction object that gives access to response properties.
-- allowed_methods: Comma-delimited list of allowed HTTP methods. (e.g. GET,POST,PUT,DELETE)
-- allowed_origins: Comma-delimited list of allowed origins. (e.g. localhost,localhost:8080,test.com)
function cors_response(txn, allowed_methods, allowed_origins)
local method = txn.sf:method()
local origin = txn:get_priv()

-- Always vary on the Origin
txn.http:res_add_header("Vary", "Accept-Encoding,Origin")

-- Bail if client did not send an Origin
if origin == nil or origin == '' then
return
end

local allowed_origins = core.tokenize(allowed_origins, ",")

txn.http:res_add_header("Access-Control-Allow-Credentials", "true")
if contains(allowed_origins, "*") then
core.Debug("CORS: " .. "* allowed")
txn.http:res_add_header("Access-Control-Allow-Origin", "*")
preflight_request(txn, method, allowed_methods)
elseif contains(allowed_origins, origin:match("//([^/]+)")) then
core.Debug("CORS: " .. origin .. " allowed")
-- Remove existing header, otherwise it can be present twice
txn.http:res_del_header("Access-Control-Allow-Origin")
-- Add header with the right value
txn.http:res_add_header("Access-Control-Allow-Origin", origin)
preflight_request(txn, method, allowed_methods)
else
core.Debug("CORS: " .. origin .. " not allowed")
end
end

-- Register the actions with HAProxy
core.register_action("cors", {"http-req"}, cors_request, 0)
core.register_action("cors", {"http-res"}, cors_response, 2)

File haproxy/haproxy.cfg.tmpl:

global
log /dev/log local2
maxconn 4000
max-spread-checks 500 #ms between heathchecks of different services
tune.ssl.default-dh-param 2048 # remove warning
lua-load /etc/haproxy/cors.lua

# allows the service IP changes to be detected (when a container is restarted, for example)
resolvers dns
nameserver docker 127.0.0.11:53
hold valid 1s


defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 10m
timeout server 10m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
default-server init-addr last,libc,none
default-server inter 60s fall 3 rise 1
default-server maxconn 50 # gunicorn is started with 5 processes and 10 threads each

# compress responses
compression algo gzip
compression type text/html text/plain text/css text/xml application/xml application/javascript application/json image/svg+xml

# Use provided example error pages
errorfile 400 /usr/local/etc/haproxy/errors/400.http
errorfile 403 /usr/local/etc/haproxy/errors/403.http
errorfile 408 /usr/local/etc/haproxy/errors/408.http
errorfile 500 /usr/local/etc/haproxy/errors/500.http
errorfile 502 /usr/local/etc/haproxy/errors/502.http
errorfile 503 /usr/local/etc/haproxy/errors/503.http
errorfile 504 /usr/local/etc/haproxy/errors/504.http


backend geoportal
option httpchk
http-check send meth GET uri ${VISIBLE_ENTRY_POINT}
http-check expect status 200
server linked geoportal:8080 resolvers dns #check


backend tilecloudchain
option httpchk
http-check send meth GET uri ${VISIBLE_ENTRY_POINT}tiles/c2c/health_check
http-check expect status 200
server linked tilecloudchain:8080 resolvers dns #check


frontend plain
bind :80

# Required variables from the request
http-request set-var(req.path) path

# If the path starts with /tiles/, use the tilecloudchain backend
acl is_tiles var(req.path) -m beg ${VISIBLE_ENTRY_POINT}tiles/
use_backend tilecloudchain if is_tiles
# Set CORS Header for authorized domains
http-request lua.cors
http-response lua.cors "OPTIONS,GET,POST" "demo.geomapfish.dev,app.localhost:8080"

# Redirect all to geoportal by default
default_backend geoportal
3. Modify the file geoportal/Dockerfile to copy the new LUA file during the build-process:
COPY --from=builder /usr/lib/node_modules/ngeo/dist/* /etc/static-ngeo/
COPY --from=builder /etc/static-ngeo/* /etc/static-ngeo/
COPY --from=builder /app/alembic.ini /app/alembic.yaml ./
# Add the following line:
COPY --from=builder /tmp/config/haproxy/cors.lua /etc/haproxy/

4. Configure the Browser

Depending on what browser you're using or what extensions you've installed, you might end up with a login not working properly. One probable cause could be that your browser does not allow third-party cookies. Please refer to your browser documentation to enable them.