Alternative SP login
by Mark Banierink
Hi all,
For our application it is important that there is an alternative login mechanism for KEYCLOAK-SAML. Wildfly/Undertow already support this mechanism for SPNEGO (SPNEGO,FORM with FORM as fallback for instance). For SAML this could be achieved by allowing an excluded URL, which is not redirecting to the IDP, showing the a form for FORM authentication.
What are your thoughts on this feature? Or is there an existing thread on this subject?
Thanks in advance.
Kind regards,
Mark
5 years, 6 months
ClientRole Import/Export , Get all ClientRoles of an User, Sharing Authorization
by Lasse Jahn
Hello,
I have 3 questions.
1. How can I export and import client roles?
(Background: I have a client and created some roles, policies,
permission... Now when I export the client the authorization data was
not included, but I could export them separately. For client roles I
could not find a way of exporting them separate. Some of the client
roles are normal roles other are composite roles.)
2. How do I get all client roles of an user?
(Background: When I look at the OIDC access token of an user, obviously
somehow all client roles can be fetched for an specific user. I need to
walk trough all client roles of an user. For realmRoles there exists an
endpoint in Admin REST api, but for client roles only one to recieve the
client roles of one specific client regarding the user. Is there some
efficient way of getting an array of client roles or something similar?)
3. Can I restrict role-mapping rights of a user to some of the client roles?
(Background: I want to enable an user to map existing client roles to
other users. Give an user the right to share roles to others can be done
this way [1]. But how can I ristrict this rights to only sharing
particular roles? Is this possible? For instance we have 5 roles admin,
share_resource1, access_resource1, share_resource2, access_resource2. A
user with the role admin shall be able to map each of this roles to
other user, user with share_resource1 shall only be able to map the role
access_resource1 but non else, analog for resource2.)
Thanks in advance for any response.
Regards Lasse
[1]
https://lists.jboss.org/pipermail/keycloak-user/2017-November/012192.html
5 years, 6 months
Spring Security and KeycloakRole (GrantedAuthority) Implementation
by Pedro Igor Silva
Hello All,
I would like to know from those using the Spring Security Adapter if we can
do a very simple change to the KeycloakRole type which is used to represent
roles granted by Keycloak.
The change [1] is all about changing the equals method to support any
instance of GrantedAuthority (parent) instead of KeycloakRole instances
only.
The reason I'm asking is that in GrantedAuthority docs there is a comment
[2] that made me wonder if we could potentially break any existing
deployment relying on the current implementation of equals, where an exact
match of KeycloakRole instance is expected.
Please, let me know your feedback. I'm OK with the proposed changes but I
would like to hear more feedback before we accept the changes.
[1] https://github.com/keycloak/keycloak/pull/6113
[2]
https://github.com/spring-projects/spring-security/blob/master/core/src/m...
Regards.
Pedro Igor
5 years, 6 months
rfc7523 - section 2.1 - jwt-bearer
by Rob Resendez
I would like to use rfc7523 in keycloak. In particular, section 2.1. https://tools.ietf.org/html/rfc7523#section-2.1
There have been some prior requests on the mailing list that are mostly unanswered. I found one that has a response, "create an issue". I couldn't find any related issues in jira. Any info on plans to implement this specification? Is it okay to open an issue - I worry about getting some of the fields (component, etc) wrong.
Rob Resendez
[cid:CPSINew_8e504092-fab6-4ce3-ab1f-37d711ec8192.PNG]
Electronic Mail Confidentiality Notice:
This electronic mail message and all attachments may contain confidential information belonging to the sender or the intended recipient. This information is intended ONLY for the use of the individual or entity named above. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution (electronic or otherwise), forwarding or taking any action in reliance on the contents of this information is strictly prohibited. If you have received this electronic transmission in error, please immediately notify the sender by telephone, facsimile, or email to arrange for the return of the electronic mail, attachments, or documents.
5 years, 6 months
When creating a user he is not added to the requested group (on only one of my instances)
by Tiago Batista
Hello all,
I have migrated an application to keycloak a couple of months ago,
however today I come to you with a problem I can not solve. The users
created on the QA environment are created as they should. They are part
of the "self-management" group as expected, however the users created
on the staging environment are not added to the group!
The thing that puzzles me is that the environments are a mirror of one
another, same docker images of the api client are promoted from our QA
to the client's staging. I have already checked and the keycloak helm
charts are on the same version and the keycloak docker images have the
same hash (6862c765d226).
The user that is invoking the API has the manage-users role on the
realm.
I would love some insight as to where should I look next as this is
puzzling me.
Below is an export of the requests from the browser. The response to
both is a 201 created, and there is no information on the logs or the
admin events regarding any failure to add the user to the group. I have
redacted the domain name and the token.
curl 'https://auth.staging.TLD/auth/admin/realms/acme/users' -H 'User-
Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:67.0) Gecko/20100101
Firefox/67.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --
compressed -H 'Referer: https://mc.acme.staging.TLD/app/members/list/'
-H 'Authorization: Bearer VALID_TOKEN' -H 'content-type: application/json' -H 'Origin: https://mc.acme.staging.TLD' -H 'Connection: keep-alive' -H 'TE:
Trailers' --data
'{"username":"tiago.batista+acmemember1(a)mindera.com","email":"tiago.bat
ista+acmemember1(a)mindera.com","firstName":"tiago","lastName":"member","
enabled":true,"groups":["self-management"]}'
curl 'https://auth.qa.TLD/auth/admin/realms/acme/users' -H 'User-Agent:
Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:67.0) Gecko/20100101
Firefox/67.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --
compressed -H 'Referer: http://mc.acme.qa.TLD/app/members/list' -H
'Authorization: Bearer VALID_TOKEN' -H 'content-type: application/json'
-H 'Origin: http://mc.acme.qa.TLD' -H 'Connection: keep-alive' -H 'TE:
Trailers' --data
'{"username":"tiago.batista+acmeqa1(a)mindera.com","email":"tiago.batista
+acmeqa1(a)mindera.com","firstName":"tiago","lastName":"member","enabled"
:true,"groups":["self-management"]}'
the token contains the following role on both environments:
"resource_access": {
"realm-management": {
"roles": [
"manage-users"
]
},
the staging environment has the following groups:
[
{
"id": "9fa577be-cf52-4f21-aa7f-770e7c1b81f3",
"name": "all-plinth-permissions",
"path": "/all-plinth-permissions",
"subGroups": []
},
{
"id": "5db60d20-3259-489c-8ac2-f684c3e7dd54",
"name": "self-management",
"path": "/self-management",
"subGroups": []
}
]
Anyone has any good idea as to where I should look next?
Regards,
Tiago
5 years, 6 months
Registrations URL
by Mark Sargent
Hi all,
I solved my previous challenge of direct linking to the key cloak registration page.
I was wondering if I should expect the login_hint to populate to work? We are using email as username option?
/auth/realms/gallagher/protocol/openid-connect/registrations?client_id=account&redirect_uri=http%3A%2F%2Flocalhost%3A8081%2F&state=71825558-a803-4393-8b2c-4cce951b4fe9&response_mode=fragment&response_type=code&scope=openid&nonce=4f1c3181-255f-4781-83d6-1849ed2321a3&login_hint=username
I have used
username
username(a)example.com
username%40example.com
Thoughts?
Cheers
Mark
________________________________
This email is confidential and may contain information subject to legal privilege. If you are not the intended recipient please advise us of our error by return e-mail then delete this email and any attached files. You may not copy, disclose or use the contents in any way. The views expressed in this email may not be those of Gallagher Group Ltd or subsidiary companies thereof.
________________________________
5 years, 6 months
authorizationSettings not in response
by Rafael Tovar.
Hi everybody,
I'm trying to get the authorization settings of a client, but its not
coming in the response of the request.
This is the request im doing:
http://localhost:8080/auth/admin/realms/master/clients/c8e32bbc-72e6-4c30...
and this is the response:
{
"id": "c8e32bbc-72e6-4c30-827f-41ee51980433",
"clientId": "api",
"surrogateAuthRequired": false,
"enabled": true,
"clientAuthenticatorType": "client-secret",
"redirectUris": [
"*"
],
"webOrigins": [],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": true,
"serviceAccountsEnabled": true,
"authorizationServicesEnabled": true,
"publicClient": false,
"frontchannelLogout": false,
"protocol": "openid-connect",
"attributes": {
"saml.assertion.signature": "false",
"saml.force.post.binding": "false",
"saml.multivalued.roles": "false",
"saml.encrypt": "false",
"saml.server.signature": "false",
"saml.server.signature.keyinfo.ext": "false",
"exclude.session.state.from.auth.response": "false",
"saml_force_name_id_format": "false",
"saml.client.signature": "false",
"tls.client.certificate.bound.access.tokens": "false",
"saml.authnstatement": "false",
"display.on.consent.screen": "false",
"saml.onetimeuse.condition": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"id": "97330e11-24df-40ce-9335-51d5126d4059",
"name": "Client Host",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientHost",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientHost",
"jsonType.label": "String"
}
},
{
"id": "9e45c71d-63f9-4d15-a3b2-e8064a569041",
"name": "Client ID",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientId",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientId",
"jsonType.label": "String"
}
},
{
"id": "1e3f6604-a22e-4b0b-b5d8-ffaa501c142f",
"name": "Client IP Address",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientAddress",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientAddress",
"jsonType.label": "String"
}
}
],
"defaultClientScopes": [
"web-origins",
"role_list",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
],
"access": {
"view": true,
"configure": true,
"manage": true
}
}
Thanks,
Rafael.
5 years, 6 months
Group Membership Token Mapper fails with an error in stacktrace
by Ondrej Scerba
Hi,
When I configure Group Membership Token Mapper without token claim name, it fails with following error:
10:50:07,315 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (default task-24) Uncaught server error: java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: Null key for a Map not allowed in JSON (use a converting NullKeySerializer?) (through reference chain: org.keycloak.representations.AccessToken["[anySetter]"]->java.util.HashMap["null"])
at org.keycloak.jose.jws.JWSBuilder.jsonContent(JWSBuilder.java:65)
at org.keycloak.jose.jws.DefaultTokenManager.encode(DefaultTokenManager.java:52)
at org.keycloak.protocol.oidc.TokenManager$AccessTokenResponseBuilder.build(TokenManager.java:796)
at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.resourceOwnerPasswordCredentialsGrant(TokenEndpoint.java:585)
at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.processGrantRequest(TokenEndpoint.java:190)
at sun.reflect.GeneratedMethodAccessor684.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:139)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:510)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:400)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:364)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:366)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:338)
at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:137)
at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:106)
at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:132)
at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:100)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:439)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at org.keycloak.services.filters.KeycloakSessionServletFilter.doFilter(KeycloakSessionServletFilter.java:90)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:364)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Null key for a Map not allowed in JSON (use a converting NullKeySerializer?) (through reference chain: org.keycloak.representations.AccessToken["[anySetter]"]->java.util.HashMap["null"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:285)
at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1251)
at com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1145)
at com.fasterxml.jackson.databind.ser.impl.FailingSerializer.serialize(FailingSerializer.java:35)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:705)
at com.fasterxml.jackson.databind.ser.AnyGetterWriter.getAndSerialize(AnyGetterWriter.java:62)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:723)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3905)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsBytes(ObjectMapper.java:3243)
at org.keycloak.util.JsonSerialization.writeValueAsBytes(JsonSerialization.java:67)
at org.keycloak.jose.jws.JWSBuilder.jsonContent(JWSBuilder.java:63)
... 74 more
I think that token claim name validation should be added or this token mapper should work correctly without token claim name as others does.
Thanks,
Ondrej
5 years, 6 months
Need help related to SSO session idle and SSO session idle max
by Khyati Kataria
Hi,
I have one doubt related to these two fields: SSO session idle and SSO
session idle max in keycloak token settings
In our case it is been 30 minutes set as session idle , so webpage
should get logout within 30 minutes. But we can observe it is not
logging out after described time. We have also tried by setting to 2-3
minutes, but it’s not happening.
But, when we set Session_max to 3 minute, then webpage is getting
logout after 3 minutes.
So, basically it is taking session_max but session_idle is not working
in our project.
Could anyone please explain this behavior ? As per my understanding
session idle time is not working. or I am not sure Is this expected
behavior of keycloak or not ?
Thanks in advance !
Regards,
Khyati Kataria
5 years, 6 months
Automatic one-time login
by Manuel Bleichenbacher
Hi everybody,
Our new application will have a length user registration procedure that we would like to keep separate from Keycloak (it will mainly require application data, reuse part of the application's UI etc.). At the end of the registration procedure, we would still like to automatically login the user so he/she doesn't need to reenter the username and password. What are our options to achieve it?
The backend will certainly use a privileged Keycloak user (e.g. to create the new user via the REST Admin API). And the result of the login should be that the user's access token (JWT) is available in the user's session in the backend. So far we have come up with the following options:
Option A: REST endpoint / custom authenticator
- Add a custom REST endpoint so the backend can create a one-time code and save it as a credential on the new user
- Add a custom authenticator to the browser flow that checks for the one-time code
- The user would then be directed to the regular authentication flow. If the one-time code is present and correct, the username/password step and possibly all other authentication steps will be skipped and the authentication immediately succeeds.
Option B: REST endpoint / action token
- Add a custom REST endpoint so the backend can create a one-time code and save it as a credential on the new user
- Implement an additional action token handler that checks the one-time token to authenticate the user
- The user would then be directed to the action token URL. If the one-time code is correct, the user is authenticated.
What is your recommendation:
- Are these feasible options?
- Are we missing a good option?
For option A: How can we pass the one-time code to the authentication flow? Can we use a URL parameter, HTTP header attribute?
For option B: Can the action token flow initiate the OAUTH2 flow and return an authorization code? Can this flow be easily integrated with the Spring Boot adapter?
Any input is appreciated.
Thanks
Manuel
--
manuel bleichenbacher | senior consultant
m +41 79 617 90 01 | manuel.bleichenbacher(a)acrea.com
acrea ag | konradstrasse 32 | 8005 zürich
www.acrea.com | blog.acrea.com
www.nezasa.com - incubated by Acrea
5 years, 6 months