Authentication¶
Supported standards¶
OpenID Connect: as client, to be able to connect to an external OpenID Connect (OIDC) server.
TOTP: for two-factor authentication (2FA), this can be used for example with Google Authenticator.
OAuth2 as server: An external application can use GeoMapFish as a single sign-on (SSO) for the authentication, even if it was initially implemented to be able to connect from QGIS desktop on an application that requires two factor authentication.
The default policy¶
By default, c2cgeoportal
applications use an auth ticket authentication
policy (AuthTktAuthenticationPolicy
). With this policy, the user name is
obtained from the “auth ticket” cookie set in the request.
The policy is created, and added to the application’s configuration, in the
application’s main __init__.py
file.
In the file env.project
, you can configure the policy with the following variables:
AUTHTKT_TIMEOUT
: Default to one day.
AUTHTKT_REISSUE_TIME
: Default to 2h30, recommended to be 10 times smaller than AUTHTKT_TIMEOUT
.
AUTHTKT_MAXAGE
: Default to one day, good to have the same value as AUTHTKT_TIMEOUT
.
AUTHTKT_SECRET
: Should be defined
AUTHTKT_COOKIENAME
: Should be defined
AUTHTKT_HTTP_ONLY
: Default to true
.
AUTHTKT_SECURE
: Default to true
.
AUTHTKT_SAMESITE
: Default to Lax
.
Note
With the default configuration, for security reasons, the authentication will only work if the project is
served on https
.
See also the official documentation.
Using another policy¶
When using AuthTktAuthenticationPolicy
, an “auth ticket” cookie should be
set in the request for the user to be identified. In some applications, using
a custom identification mechanism may be needed instead, for instance to use SSO.
User validation¶
For logging in, c2cgeoportal
validates the user credentials
(username/password) by reading the user information from the user
database
table. If a c2cgeoportal application should work with another user information
source, like LDAP, a custom client validation mechanism can be set up.
Basic auth¶
To be able to access the OGC services from your desktop GIS, you should enable the basic authentication
by setting BASICAUTH
to True
in the env.project
file.
To force the application to ask for a password, you should have the attribute authentication_required
in your query string.
Note
For security reasons, basic authentication and two factor authentication should not be enabled together.
Two factors authentication¶
GeoMapFish support TOTP (Time-Based One-Time Password Algorithm) two factors authentication (RFC 6238). To enable the two factors authentication you should set the following settings:
vars:
authentication:
two_factor: true
two_factor_issuer_name: <used_issuer_name>
If a user lost his second authentication factor he can’t ask for a new one, to reset it the administrator should uncheck the ‘The user changed his password’ field on the user in the admin interface.
Note
For security reasons, basic authentication and two factor authentication should not be enabled together, you should use OAuth2 for that.
Account lockout¶
To lock an account after a certain number of authentication failures, set the following settings:
vars:
authentication:
max_consecutive_failures: 10
To unlock a user, the administrator should uncheck the ‘Deactivated’ field on the user in the admin interface.
Intranet¶
To configure the intranet networks fill in the configuration like:
vars:
intranet:
networks:
- 192.168.1.0/24
- 192.168.1.0/255.255.255.0
- 192.168.1.0/0.0.0.255
- 2001:db00::0/24
- 2001:db00::0/ffff:ff00::
See Python documentation.
Note
Intranet detection is provided to improve usability for web site usage within the Intranet; however, please be aware that Intranet detection is not a secure mechanism. To secure access to sensitive data, do not rely on Intranet detection; for that, you must use user authentication.
A user can easily manually set the Forwarded or X-Forwarded-For header to spoof his IP.
Lost admin password¶
You can generate a new admin password the following command:
Reset a user password. The username is used as password if the password is not provided with the corresponding option. User can be created if it does not exist yet.
usage: docker compose exec geoportal manage-users [-h] [--password PASSWORD]
[--create]
[--rolename ROLENAME]
[--email EMAIL]
[config_uri]
[config_vars ...] user
Positional Arguments¶
- config_uri
The URI to the configuration file.
Default:
'c2c://geoportal/development.ini'
- config_vars
Variables required by the config file. For example, http_port=%(http_port)s would expect http_port=8080 to be passed here.
Default:
()
- user
The user
Named Arguments¶
- --password, -p
Set password (if not set, username is used as password
- --create, -c
Create user if it does not already exist
Default:
False
- --rolename, -r
The role name which must exist in the database
Default:
'role_admin'
- --email, -e
The user email
External application¶
Some service of GeoMapFish has some host restriction if you mix the domain.
Application authentication¶
To be considered as authenticated we should have the correct Cookie
header,
we also check the Referer
header to be sure that the user is coming from the same domain.
If he is equals to the Host
header, we consider that the user is coming from the same domain.
If your server and client application are not on the same domain, to make the login working,
you should add the client application domain name (with port) in the vars in vars/authorized_referers
.
This check is also done on the came_from
parameter during the login process.
Shortener¶
If you use the shortener service to create link on application on another domain name, you should add
this domain name in the vars in vars/shortener/allowed_hosts
.
Admin¶
We provide a view for the admin interface, to be able to clear the cache per OGC server.
If for an unknown reason you have not the same host in the Host
header and came_from
parameter, you should
add the domain of the came_from
parameter in the vars in vars/admin_interface/allowed_hosts
.
OAuth2 with QGIS¶
In the admin interface create an ‘OAuth2 Client’ with:
Client ID
as e.-g. ‘qgis’fill the
Secret
Redirect URI
as ‘http://127.0.0.1:7070/’
On QGIS:
Add an
Authentication
Set a
Name
Set
Authentication
toOAuth2
Set
Grant flow
toAuthentication code
Set
Request URL
to<geomapfish_base_url>/oauth/login
Set
Token URL
to<geomapfish_base_url>/oauth/token
Set
Client ID
to ‘qgis’Set
Client secret
to the secret
Note
For security reason a user can only have one active session per client.
If you need to have more than one active session you should provide more than one client.
OpenID Connect¶
We can configure an OpenID connect service as an SSO (Single Sign-On) provider for our application. This allows users to log in to our application using their OpenID Connect credentials.
We use OpenID Connect Discovery 1.0 with an Authorization Code Flow from OpenID Connect Core 1.0, with PKCE (Proof Key for Code Exchange, RFC 7636).
sequenceDiagram actor User participant Browser participant Geoportal participant IAM Geoportal->>IAM: discovery endpoint User->>+Browser: Login Browser->>+Geoportal: Login Geoportal->>-Browser: redirect Browser->>+IAM: authorization endpoint IAM->>-Browser: redirect Browser->>+Geoportal: callback endpoint Geoportal->>IAM: token endpoint opt on using user info instead of jwt token Geoportal->>IAM: userinfo endpoint end Geoportal->>-Browser: authentication data in cookie Browser->>-User: Reload Browser->>+Geoportal: any auth endpoint opt on token expiry Geoportal->>IAM: refresh token endpoint end Geoportal->>-Browser: response
Authentication provider¶
If we want to use OpenID Connect as an authentication provider, we need to set the environment variable
OPENID_CONNECT_ENABLED
to true
, then we need to set the following configuration in our
vars.yaml
file:
vars:
authentication:
openid_connect:
url: <the service URL>
client_id: <the client application ID>
With that the user will be create in the database at the first login, and the access right will be set in the GeoMapFish database. The user correspondence will be done on the email field.
Other options¶
client_secret
: The secret of the client.
trusted_audiences
: The list of trusted audiences, if the audience provided by the id-token is not inthis list, the
ID token
will be rejected.
scopes
: The list of scopes to request, default is [openid
, profile
, email
].
query_user_info
: Iftrue
, the OpenID Connect provider user info endpoint will be requested toprovide the user info instead of using the information provided in the
ID token
, default isfalse
.create_user
: Iftrue
, a user will be create in the geomapfish database if not exists,default is
false
.login_extra_params
: Extra parameters to add to the login request.See Zitadel additional parameters. Default is
{}
.
match_field
: The field to use to match the user in the database, can be username
(default) or email
.
update_fields
: The fields to update in the database, default is:[]
, allowed values areusername
,display_name
andemail
.user_info_fields:
The mapping between the user info fields and the user fields in the GeoMapFish database,the key is the GeoMpaFish user field and the value is the field of the user info provided by the OpenID Connect provider, default is:
username: sub display_name: name email: email
Example with Zitadel¶
vars:
authentication:
openid_connect:
url: https://sso.example.com
client_id: '***'
query_user_info: true
create_user: true
Hooks¶
If you want to redefine the user creation process, you can use the hooks get_remember_from_user_info
and get_user_from_remember
.
get_remember_from_user_info
: This hook is called during the user is authentication.
The argument are the pyramid request
, the received user_info
, and the remember_object
dictionary
to be filled and will be stored in the cookie.
get_user_from_remember
: This hook is called during the user is certification.
The argument are the pyramid request
, the received remember_object
, and the update_create_user
boolean.
The return value is the user object User
or DynamicUsed
.
The update_create_user
will be True
only when we are in the callback endpoint.
Full signatures:
def get_remember_from_user_info(request: Request, user_info: Dict[str, Any], remember_object: OidcRememberObject) -> None:
def get_user_from_remember(request: Request, remember_object: OidcRememberObject, update_create_user: bool) -> Union[User, DynamicUsed]:
Configure the hooks in the project initialization:
def includeme(config):
config.add_request_method(get_remember_from_user_info, name="get_remember_from_user_info")
config.add_request_method(get_user_from_remember, name="get_user_from_remember")
QGIS with Zitadel¶
In Zitadel you should have a PKCE application with the following settings:
Redirect URI: http://127.0.0.1:7070/
.
You need QGIS desktop 3.34 or later.
On QGIS:
Add an
Authentication
.Set a
Name
.Set
Authentication
toOAuth2
.Set
Grant flow
toPKCE authentication code
.Set
Request URL
to<zitadel_base_url>/oauth/v2/authorize
.Set
Token URL
to<zitadel_base_url>/oauth/v2/token
.Set
Client ID
to<client_id>
.Set
Scope
to theopenid profile email
.
Implementation¶
When we implement OpenID Connect, we have to possibilities:
Implement it in the backend.
Implement it in the frontend, and give a token to the backend that allows to be authenticated on an other service.
In c2cgeoportal we have implemented booth method.
The backend implementation is used by ngeo an the admin interface, where se store the user information
(including the access and refresh token) in an encrypted JSON as a cookie.
To use the backend implementation, the /oidc/login
endpoint should be called with
an optional came_from
parameter to redirect the user after the login.
The frontend implementation is used by application like QGIS desktop, on every call the Bearer Token should be provided in the Authorization header, we have to call the user info endpoint to get the user information.