Link with Patreon

Context
I am adding user accounts to my website with the help of keycloak. Registration and Login works. Now I try to provide a button to link a patreon account, so users can get more features if they are supporting me on patreon.com. I wanted to use the keycloak social link feature and thought I don’t need to save any additional data in an own database (beside the stuff keycloak is persisting about my users).

The problem is, that the patreon API is not using OIDC (but OAuth and an /identity endpoint) and keycloak on the other side always sends the ‘openid’ scope, leading to errors on the patreon side.

I already did a lot of research but I am unsuccessful in a way that makes me think I miss something obvious.
I thought both patreon and keycloak are quite common so I would think there could be a provider already included in keycloak. But I don’t even find any community stuff by searching the web.

Specific questions

  • Does anyone know of an implementation connecting keycloak<->patreon?
  • When I do the connection myself - Can I still save the patreon data to keycloak?
    I can read the Patreon API myself on the backend of my app. I could save the patreon user data and refresh tokens in an own database but it would be great if I could somehow add that data directly to the user account on keycloak instead. Is this possible?
  • Additional questions that seem like I should change the whole approach
    When I do the API communication with patreon myself, I need to do it on the server as it needs a clientId and secret but at the moment I only do the communication with my keycloak on the frontend (public client with pkce).
    As I can’t add the Bearer Token as a Header to the redirect coming from patreon, the token with data about the user logged in to my site needs to be transmitted somehow else (I need both the patreon data and the logged in user to be able to connect them). For example, after the login is done on the frontend I could set a cookie. It wouldn’t be httpOnly but as frontend JS is doing everything at the moment XSS attacks are problematic anyway. I guess I should switch to a server side auth and a session to get rid of the XSS problem and be able to connect patreon and my user account more easily. This all seem to be more complex than necessary but I don’t see an easy alternative despite the use case that seems pretty common to me.
    Writing a patreon identity provider for keycloak in java seems like the other right thing to do, but then it would probably have been done already?!

If you want to implement your own identity provider, it looks like there is enough in the Patreon OAuth2 implementation + User identity resource to create one. Look at the GitHub identity provider code for an example keycloak/services/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java at main · keycloak/keycloak · GitHub

Thanks @xgp for your answer and the links!
I am now implementing an identity provider and am already getting the json data from patreon. I am not using the link to create new users but only to connect existing users with patreon accounts.

It would be great to get some tipps on how to save/map the patreon data!

  • Data like name, email etc is not important for me and I am not retrieving them at the moment.
  • What is important is how much the user is paying me on patreon (or maybe to which tier/membership he subscribed).

Here is an example json I get from patreon

{
	"data": {
		"attributes": {},
		"id": "123456789",
		"relationships": {
			"memberships": {
				"data": [
					{
						"id": "e4d8ae89-...",
						"type": "member"
					}
				]
			}
		},
		"type": "user"
	},
	"included": [
		{
			"attributes": {
				"campaign_lifetime_support_cents": 100,
				"patron_status": "active_patron"
			},
			"id": "e4d8ae89-...",
			"relationships": {
				"currently_entitled_tiers": {
					"data": [
						{
							"id": "1231231",
							"type": "tier"
						}
					]
				}
			},
			"type": "member"
		},
		{
			"attributes": {
				"amount_cents": 100,
				"title": "Test Supporter",
				"url": "/checkout/myurl?rid=1231231"
			},
			"id": "1231231",
			"type": "tier"
		}
	],
	"links": {
		"self": "https://www.patreon.com/api/oauth2/v2/user/123456789"
	}
}

I guess it makes sense to not only save that data to keycloak but map that to a role. This way I could add multiple social providers in the future and don’t need to know them in my apps. Features would be available because of roles.
Nevertheless it would be great to see the patreon data in the JWT as well to display something like “because you pay me 3eur per month on patreon you can xyz” on the website and add a link to his patreon account or similar

Does this sound plausible or am I thinking in a wrong direction?
Any examples for that?

Thanks a lot!

I think that’s the right direction. I would suggest looking at the other social providers for examples of what other implementations do with the data they get back from the provider. Then start with the simplest possible set.

Thanks a lot again @xgp!!

Any idea why I can only select one of the following 3 mappers for my custom identity provider in the keycloak admin gui?

  • Hardcoded Attribute
  • Hardcoded Role
  • Hardcoded User Session Attribute

I need a mapper that sets a role depending on the values in the json retrievd from the linked IDP.
Which Mapper would fit to set a role if
"patron_status" == "active_patron"
AND
"amount_cents" > 300

in the example json above and how can I make that mapper be selectable?


I call AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, patreonJson, getConfig().getAlias()); in my Provider. I guess that’s enough to let the Mapper have access to the data!?

I just tested “Add Mapper” with a github and facebook IDP and both have those 3 “Hardcoded…” Mappers and two others (Attribute Importer, Username Template Importer).

I thought a role is the thing to add but I guess an attribute works as well, as long as it comes up in the Keycloak JWT. So I guess the “Attribute Importer” would be one way to make that work

I’d also take a look at writing your own IdentityProviderMapper. Take a look at AbstractIdentityProviderMapper (Keycloak Docs Distribution 23.0.7 API) which has a lot of base classes for mapping into Group/Role/Attribute/etc. There will be example implementations for how to use them in the code for the Keycloak social providers keycloak/services/src/main/java/org/keycloak/social at main · keycloak/keycloak · GitHub

As a first test I tried to add a mapper like the one of the github provider. I added a class PatreonAttributeMapper.java with the following content

public class PatreonAttributeMapper extends AbstractJsonUserAttributeMapper {
    
    public static final String MAPPER_ID = "patreon-user-attribute-mapper";
	private static final String[] cp = new String[] { PatreonIdentityProviderFactory.PROVIDER_ID };

	@Override
	public String[] getCompatibleProviders() {
		return cp;
	}

	@Override
    public String getId() {
        return MAPPER_ID;
    }
    
}

I still only get those same 3 Mappers in the admin GUI.
Any idea what is missing to add more mappers (my mapper) in the dropdown?

I did another test

  1. Added the Hardcoded Role Mapper to my provider in the admin gui and selected a test role.
  2. Unlinked and linked my test user again

I thought now the user would have that test role but it does not. Any idea why?

It’s hard to tell without seeing all of your code, but have you registered the mapper in the META-INF/services /org.keycloak.broker.provider.IdentityProviderMapper file?

Thanks a lot!! With this change the mapper is now selectable in the drop down!

Is this documented somewhere? Maybe I have overlooked some docs that can help?

My mapper is active but I am not successful with adding the role.
I use HardcodedRoleMapper as an Example. It adds the role in importNewUser and in updateBrokeredUser

I have added log messages in my mapper to

  1. preprocessFederatedIdentity
  2. importNewUser
  3. updateBrokeredUser

only the first is called. But this method gets no UserModel. The HardcodedRoleMapper adds the role via UserModel->grantRole

I found the method context.addMapperGrantedRole(nameOfRoleToAdd) but I saw in AbstractAttributeToRoleMapper that this is also used in updateBrokeredUser.

  • Should updateBrokeredUser be called?

Note: I don’t use the external IDP to import new users but do client initiated account linking.

I see in the code that it should be called in IdentityProviderMapperSyncModeDelegate->delegateUpdateBrokeredUser if the sync mode is FORCE. It is FORCE…I log that.

I am working backwards through the code from there, but maybe you have a better idea or know why updateBrokeredUser is not called.

Thanks a lot again for your help!

Yes, it’s in the Server Developer documentation. E.g. Server Developer Guide

This is a good example of really needing to see the code in order to be able to help. I roughly follow your questions in this message, but it would be impossible to really help without seeing the code and being able to test.

I found a keycloak bug while stepping through the code and I also found the fix that was added 9 months ago.
I don’t know why but my docker pulls an old keycloak image (21.0.2) when I use the “latest” tag in my Dockerfile. Now that I use “24.0” instead, it works and my role is added.

Thanks for your previous answers. i will publish my provider to github if I have other questions regarding my code.

Current state
When the user links the patreon account to his keycloak account I get all necessary data and can add a role and additional attributes to his profile.

Question
I thought the Post login flow can be used to let keycloak automatically update the ‘cached state’ of the brokered IDP. I thought keycloak saves the access and refresh token of the IDP and calls the provider/mapper when a linked user logs in.
But after re-reading the docs I think this was a misinterpretation.
What’s the easiest way to reevaluate the linked data from time to time, e.g. when the user logs in?

Context
Users can pay a monthly fee at patreon and will have more features on my website. Of course I want to recognize when a user does not pay anymore and update the data saved in my keycloak (eg remove an added role).
If I would have to retrieve those updates myself, I guess the whole Mapper makes no sense for me, right?

To be more specific. I don’t want the user to redo the linking after each login.

So I guess I need a different flow for “first login flow” and “post login flow”, right?

Is there a fitting “post login flow” already selectable by configuration? So that keycloak gets the data of the brokered IDP after each login and uses my mapper again to update the cached user data?

Or do I need to provide an own EventListener to listen for login events, talk with the brokered idp and update the usermodel?
Does it make sense in your opinion to add some kind of recurring job in my custom IdentityProvider that reads from the poatreon API lets say once a day and update cached user data?

By the way, my Sync Mode is still “Force”. Shouldn’t this setting alone be enough to let keycloak …

  • call the external IDP again
  • update the user data via my mappers updateBrokeredUser

… after each user login?!

My provider on github:

If the update is only done, when the user does the login via this IDP:
I guess it would make sense to add another sync mode then. At least patreon is a great use case for that, it should be updated on login also if the account is only linked. The naming could get interesting. ‘Force’ already sounds like it should do it.