Code Monkey home page Code Monkey logo

Comments (14)

sd-f avatar sd-f commented on May 29, 2024 1

from keycloak-custom-attribute-idp-linking.

sd-f avatar sd-f commented on May 29, 2024 1

Now I get the problem! I will have a deeper look in keycloak code if there might be a workaround, but it seems unlikely.

from keycloak-custom-attribute-idp-linking.

sd-f avatar sd-f commented on May 29, 2024 1

@bernhardriegler It seems i will have to implement some new features in my plugin, hopefully i can get to it soon, i'll get back to you if i had some time to test.

from keycloak-custom-attribute-idp-linking.

sd-f avatar sd-f commented on May 29, 2024

Hi Bernhard! I now had some time to analyze your question. I am sorry to tell you that most of the users of my plugin handle "first broker login" somewhere else with external tools and use "Auto link ..." to link external (id austria) accounts to their local/federated users. That means the attribute will be written outside of keycloak to an external database. I also implemented such a tool myself some time ago and when i remember correctly, the sub will be the eID of your users. Have you tried setting up a "Custom mapper" for the eid provider client (e.g. attribute: "eid" claim sub)? If that is not working, I quess the only way would be to extend the "First Broker login Flow", which means writing some code :(

from keycloak-custom-attribute-idp-linking.

bernhardriegler avatar bernhardriegler commented on May 29, 2024

Hi Lucas,

thank you very much for your reply.
sub does contain a random value, while there is another claim in the token that is relevant from now on.
Austria ID changed this in end of May 2023.

Here is their official announcement:
https://www.oesterreich.gv.at/id-austria/Betriebsinformationen-f%C3%BCr-ID-Austria-Service-Provider/Archiv-Mailings-2023/%C3%84nderungen-f%C3%BCr-OIDC-Endpoint.html

Regarding the first broker login - I am able to read the token form Austria ID there. But how would I know which user should be linked to the current Austria ID authentication?
I could ask users to enter their credentials to match the local user to the just authenticated Austria ID user.
I think this flow would feel somewhat strange.
I would prefer users to first login locally and then authenticate with Austria ID. I am lost at the point how to add a custom behaviour at this point of the flow.

from keycloak-custom-attribute-idp-linking.

sd-f avatar sd-f commented on May 29, 2024

Hi Bernhard, if the user is already authenticated on your keycloak (locally) and starts account linking via the "account console app" (provided from keycloak) then keycloak will manage the linking on its own (federated user with identity provider links, can be seen in the user details view). This works with default authentication configuration out of the box and no attribute mapping is needed. Like Social login (e.g. google). My plugin is only necessary if you already have eID information in your local user database and no account link in keycloak. If you want to save eID information for example the bpk claim you can try to configure an import attribute mapping https://github.com/sd-f/keycloak-custom-attribute-idp-linking/tree/main#optional-check-your-external-provider-attribute-mapping. I hope we are gettings closer to what you need. cheers luke

from keycloak-custom-attribute-idp-linking.

bernhardriegler avatar bernhardriegler commented on May 29, 2024

It looks like Keycloak will use the value of claim sub for the account link.
I checked on my local installation. In the user session the user which has the account linked has the value which was in the claim sub under identity provider link.

While this works for other identity providers, where the value of sub is always the same for each user, this does not work with Austria ID. subwill change with each login.
The claim urn:pvpgvat:oidc.bpk should be used.

I tried to apply a mapper to somehow change what is used for the identity provider link - without success.

Here in the keycloak source code the account linking happens:
https://github.com/keycloak/keycloak/blob/22.0.1/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java#L952

If there would be a way to tell keycloak which claim to use for the identity link, this would solve my issue.

from keycloak-custom-attribute-idp-linking.

rasos avatar rasos commented on May 29, 2024

Keycloak uses the username or e-mail to map users that come back again.

I could verify, that if you use the normal openid profile scopes https://eid.oesterreich.gv.at/.well-known/openid-configuration the mapped username seems to change with your next eID login.

You could try to map the bpk attribute to the username.

bpkmapper

from keycloak-custom-attribute-idp-linking.

bernhardriegler avatar bernhardriegler commented on May 29, 2024

Thank you for still trying to figure this out with me.
I tried something similar with the "User Template Importer" already.
I also tried it now with the suggested "Attribute Importer".

Nothing changed.

It looks like the mappers are not applied when linking accounts.

from keycloak-custom-attribute-idp-linking.

sd-f avatar sd-f commented on May 29, 2024

@bernhardriegler On the way while evaluating this situation we found some other interesting facts you can try (because i am still waiting for my accounts for id austria).

if you use another mapper like in the screenshot above and use "sub" in "user attribute name" you should be able to get the same behaviour like before the the change they made with using a random subject. your users will get a token container the same subject all the time. If that is not working, you can also override the "preferred_username" attribute, which keycloak uses to map the user. After that the id provider link should always be the same (maybe in combination with my plugin, if you have existing bpk info in your user database).

Again if i get my test setup i will have a further look, how this can be handled in a more clean way.

Don't forget to use both "detect" and "set" steps in your athentication flow as well as set all properties in your mapper.

Sorry that I currently have no environment to test all that things myself with eID, hope in the next days the letter will arrive at my house ;)

cheers luke

from keycloak-custom-attribute-idp-linking.

rasos avatar rasos commented on May 29, 2024

I have been notified that the right syntax to do a search within a claim is:

Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'. To use dot (.) literally, escape it with backslash (.).

Therefore, for eID Austria to reference for bPK this should work:

         urn:pvpgvat:oidc\.bpk

from keycloak-custom-attribute-idp-linking.

sd-f avatar sd-f commented on May 29, 2024

@rasos that sounds great, @bernhardriegler could you give this another try ;)

from keycloak-custom-attribute-idp-linking.

bernhardriegler avatar bernhardriegler commented on May 29, 2024

I tried this escaped mapping already - this did not change the username or identity provider link value.

However I found a workaround to extract the bpk from the claim when a user is linking an account.
I did setup a custom event handler like this:

package at.br.keycloak.event;

import org.jboss.logging.Logger;
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.json.*;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

import at.br.keycloak.CustomProvider;

/**
 * Event listener implementation
 */
public class CustomEventListener implements EventListenerProvider {
	private final static Logger logger = Logger.getLogger(CustomEventListener.class);

	private KeycloakSession session;

	public CustomEventListener(KeycloakSession session) {
		this.session = session;
	}

	@Override
	public void close() {
	}

	@Override
	public void onEvent(Event event) {
		switch (event.getType()) {
		case LOGIN:
			session.userFederatedStorage().removeFederatedIdentity(session.realms().getRealm(event.getRealmId()), event.getUserId(), event.getDetails().get("identity_provider"));
			break;
		case FEDERATED_IDENTITY_LINK:
			RealmModel realm = session.realms().getRealm(event.getRealmId());
			UserModel user = session.users().getUserById(event.getUserId(), realm);
			String token = session.users().getFederatedIdentity(user,"oidc", realm).getToken();
			String logMessage = String.format("Retrieved token for user %s from OIDC provider: %s", user.getUsername(), token);
			logger.info(logMessage);
			String bpk = extractBPKFromToken(token);

			setIdentityLink(event.getUserId(), event.getRealmId(), bpk);
			
			FederatedIdentityModel federatedIdentityModel = session.userFederatedStorage().getFederatedIdentity(event.getUserId(), event.getDetails().get("identity_provider"),session.realms().getRealm(event.getRealmId()));

			break;
		default:
			break;
		}
	}

	@Override
	public void onEvent(AdminEvent event, boolean includeRepresentation) {}

	public String extractBPKFromToken(String json){
		JSONObject obj = new JSONObject(json);
		String idToken = obj.getString("id_token");
		try {
			// Split the JWT into its three parts: Header, Payload, and Signature
			String[] parts = idToken.split("\\.");

			if (parts.length == 3) {
				// Decode the Payload (second part)
				String payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8);
				//System.out.println("Decoded Payload: " + payloadJson);
				JSONObject payload = new JSONObject(payloadJson);
				return payload.getString("urn:pvpgvat:oidc.bpk");
			} else {
				logger.error("Invalid JWT format. It should have three parts separated by dots.");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Set the identity link
	 * @param userId user ID
	 * @param realmId realm ID
	 * @param link federated identity
	 */
	private void setIdentityLink(String userId, String realmId, String link) {
		logger.debug("Set identity link from EVENT LISTENER");
		logger.debug(link);
		UserModel user = session.users().getUserById(userId, session.realms().getRealm(realmId));
		if (user != null) {
			user.setSingleAttribute(CustomProvider.FEDERATED_IDENTITY, link);
		}
	}
}

This will extract the token and write it to the already logged in existing users attributes.
From here your library can be used to find a user with this attribute.

Originally I did not know that I could retrieve the token of the user from the identity provider like this:

session.users().getFederatedIdentity(user, "oidc", realm).getToken();

Thank you so much for your time and effort.

from keycloak-custom-attribute-idp-linking.

sd-f avatar sd-f commented on May 29, 2024

@bernhardriegler that sounds awesome! If you don't mind I might use some of your code myself if I run in the same problems in the future? Thank you too

from keycloak-custom-attribute-idp-linking.

Related Issues (1)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.