ivangfr / keycloak-clustered

Keycloak-Clustered extends quay.io/keycloak/keycloak official Keycloak Docker image by adding JDBC_PING discovery protocol.
162 stars 57 forks source link

Access token can not be shared. #25

Closed 301lj closed 7 months ago

301lj commented 7 months ago

Hello, Could you help me check this issue? Based on what you provided, I tried and found that although the session can be shared, the access token cannot be shared. Is this a problem caused by my configuration such as cache-ispn-jdbc-ping.xml?

docker run --name keycloak-1 -d -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -e KC_DB=mysql -e KC_DB_URL=jdbc:mysql://192.168.56.107:3306/keycloak -e KC_DB_USERNAME=root -e KC_DB_PASSWORD=secret -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-1 -e KC_CACHE_CONFIG_FILE=cache-ispn-jdbc-ping.xml -v ${PWD}/cache-ispn-jdbc-ping.xml:/opt/keycloak/conf/cache-ispn-jdbc-ping.xml --network keycloak-net quay.io/keycloak/keycloak:22.0.5 start --auto-build --http-enabled=true --hostname-strict-backchannel=false --hostname-strict=false --https-client-auth=none --proxy=edge --metrics-enabled=true

docker run --name keycloak-2 -d -p 8081:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -e KC_DB=mysql -e KC_DB_URL=jdbc:mysql://192.168.56.107:3306/keycloak -e KC_DB_USERNAME=root -e KC_DB_PASSWORD=secret -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-2 -e KC_CACHE_CONFIG_FILE=cache-ispn-jdbc-ping.xml -v ${PWD}/cache-ispn-jdbc-ping.xml:/opt/keycloak/conf/cache-ispn-jdbc-ping.xml --network keycloak-net quay.io/keycloak/keycloak:22.0.5 start --auto-build --http-enabled=true --hostname-strict-backchannel=false --hostname-strict=false --https-client-auth=none --proxy=edge --metrics-enabled=true

step1: get the access token from 8080 curl -X POST http://192.168.56.107:8080/realms/quickstart/protocol/openid-connect/token \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'client_id=authz-servlet&client_secret=secret' \ -d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token'

step2: get the RPT from 8081, but it returns null. curl -X POST http://192.168.56.107:8081/realms/quickstart/protocol/openid-connect/token \ -H "Authorization: Bearer ${access_token}" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \ --data "audience=authz-servlet"

this is my cache-ispn-jdbc-ping.xml:

<?xml version="1.0" encoding="UTF-8"?> <infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:14.0 http://www.infinispan.org/schemas/infinispan-config-14.0.xsd" xmlns="urn:infinispan:config:14.0">

<jgroups>
    <stack name="postgres-jdbc-ping-tcp" extends="tcp">
        <TCP external_addr="${env.JGROUPS_DISCOVERY_EXTERNAL_IP:127.0.0.1}" />
        <JDBC_PING
            connection_driver="com.mysql.cj.jdbc.Driver"
            connection_username="root"
            connection_password="secret"
            connection_url="jdbc:mysql://192.168.56.107:3306/keycloak"
            initialize_sql="CREATE TABLE IF NOT EXISTS JGROUPSPING (own_addr varchar(200) NOT NULL, cluster_name varchar(200) NOT NULL, bind_addr varchar(200) NOT NULL, updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, ping_data varbinary(5000) DEFAULT NULL, PRIMARY KEY (own_addr, cluster_name)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"
            insert_single_sql="INSERT INTO JGROUPSPING (own_addr, cluster_name, bind_addr, updated, ping_data) values (?, ?, '${env.JGROUPS_DISCOVERY_EXTERNAL_IP:127.0.0.1}', NOW(), ?);"
            delete_single_sql="DELETE FROM JGROUPSPING WHERE own_addr=? AND cluster_name=?;"
            select_all_pingdata_sql="SELECT ping_data, own_addr, cluster_name FROM JGROUPSPING WHERE cluster_name=?;"
            info_writer_sleep_time="500"
            remove_all_data_on_view_change="true"
            stack.combine="REPLACE"
            stack.position="MPING"
        />
    </stack>
</jgroups>

<cache-container name="keycloak">
    <transport lock-timeout="60000" stack="postgres-jdbc-ping-tcp"/>
    <local-cache name="realms" simple-cache="true">
        <encoding>
            <key media-type="application/x-java-object"/>
            <value media-type="application/x-java-object"/>
        </encoding>
        <memory max-count="10000"/>
    </local-cache>
    <local-cache name="users" simple-cache="true">
        <encoding>
            <key media-type="application/x-java-object"/>
            <value media-type="application/x-java-object"/>
        </encoding>
        <memory max-count="10000"/>
    </local-cache>
    <distributed-cache name="sessions" owners="2">
        <expiration lifespan="-1"/>
    </distributed-cache>
    <distributed-cache name="authenticationSessions" owners="2">
        <expiration lifespan="-1"/>
    </distributed-cache>
    <distributed-cache name="offlineSessions" owners="2">
        <expiration lifespan="-1"/>
    </distributed-cache>
    <distributed-cache name="clientSessions" owners="2">
        <expiration lifespan="-1"/>
    </distributed-cache>
    <distributed-cache name="offlineClientSessions" owners="2">
        <expiration lifespan="-1"/>
    </distributed-cache>
    <distributed-cache name="loginFailures" owners="2">
        <expiration lifespan="-1"/>
    </distributed-cache>
    <local-cache name="authorization" simple-cache="true">
        <encoding>
            <key media-type="application/x-java-object"/>
            <value media-type="application/x-java-object"/>
        </encoding>
        <memory max-count="10000"/>
    </local-cache>
    <replicated-cache name="work">
        <expiration lifespan="-1"/>
    </replicated-cache>
    <local-cache name="keys" simple-cache="true">
        <encoding>
            <key media-type="application/x-java-object"/>
            <value media-type="application/x-java-object"/>
        </encoding>
        <expiration max-idle="3600000"/>
        <memory max-count="1000"/>
    </local-cache>
    <distributed-cache name="actionTokens" owners="2">
        <encoding>
            <key media-type="application/x-java-object"/>
            <value media-type="application/x-java-object"/>
        </encoding>
        <expiration max-idle="-1" lifespan="-1" interval="300000"/>
        <memory max-count="-1"/>
    </distributed-cache>
</cache-container>

ivangfr commented 7 months ago

Hi @301lj Thanks for the comment. If you get a token from Keycloak on port 8080 and use it on Keycloak running on port 8081, you might have an "Invalid token issuer". Is that the case?

301lj commented 7 months ago

Yes, as you said, I can use access_token to exchange the RPT(include resources) from 8080, but cannot exchange it from 8081. This is what the 8081 log shows: WARN [org.keycloak.events] (executor-thread-1) type=PERMISSION_TOKEN_ERROR, realmId=95e930c3-ff41-4bf3-92b8-c0562015a50a, clientId=authz-servlet, userId=null, ipAddress=192.168.56.107, error=invalid_token, auth_method=oauth_credentials, grant_type=urn:ietf:params:oauth:grant-type:uma-ticket

I haven't fully figure out the mechanism here, but what I aim to achieve is the ability to get it from both 8080 and 8081. If you know how to modify it to achieve this, please let me know. Thank you!

301lj commented 7 months ago

I've solved this issue temporarily. Keycloak Admin console -> Select your Realm -> Realm settings -> General -> Frontend url and enter: http://192.168.56.107:8080

But I'm not sure if this approach is reasonable.