Flask-OIDC with keycloak - oidc_callback default callback not working
Asked Answered
L

4

6

I'm trying to use Flask-oidc in a simple flask application in order to add authentication via keycloak.

However, once I log-in with valid credentials it goes back to /oidc_callback which doesn't exist.

The flask logs show a lot of attempts of redirect with 302 result code:

127.0.0.1 - - [26/Nov/2018 10:56:54] "GET /oidc_callback?state=eyJjc3JmX3Rva2VuIjogIlluRDc0UUVLVGhRRkw5TGtuRU9RZGprNTBheVk1cERkIiwgImRlc3RpbmF0aW9uIjogImV5SmhiR2NpT2lKSVV6STFOaUo5LkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk5UQXdNUzlzYjJkcGJpSS50MVVCRUszbFBxSmZRSzkzMHB5UktBNUZibmNtU0h6TElLblgweXgtTElJIn0%3D&session_state=96eb0bd8-a4a3-49a5-a00c-f4d621cd68e0&code=eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..T5U8hwYX2ot7Llzo39-cyw.4r-lLPZ1So1j4jPqfVwW5zKgtFjMR_f38ls71SwyqrwLVnE-OfZIi0O74pgzNLQEhxFu2nT-o-7_iNuqv5EIHuaIk_mp-xAY7TlaCViM9NvEDvs78iTTmLwPHsDI20SWuPS08K1wING9CXjhZLudLsBAoWRomFHGfDI_Xyd90lb0wWa73vgcMoeatlt1sEbJTo7XxuDBg-JvyzGfqclvuh5bk848q-07tkDsTKETIK-0wLxb-vUaoqkYmqRVQ3-p.PP0YzjGpjvIqCTNCk3IZTQ HTTP/1.1" 302 -
127.0.0.1 - - [26/Nov/2018 10:56:54] "GET /login HTTP/1.1" 302 -
127.0.0.1 - - [26/Nov/2018 10:56:54] "GET /oidc_callback?state=eyJjc3JmX3Rva2VuIjogIlluRDc0UUVLVGhRRkw5TGtuRU9RZGprNTBheVk1cERkIiwgImRlc3RpbmF0aW9uIjogImV5SmhiR2NpT2lKSVV6STFOaUo5LkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk5UQXdNUzlzYjJkcGJpSS50MVVCRUszbFBxSmZRSzkzMHB5UktBNUZibmNtU0h6TElLblgweXgtTElJIn0%3D&session_state=96eb0bd8-a4a3-49a5-a00c-f4d621cd68e0&code=eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..JpVESxYMF7ApS07y_cOxmA.FRX0kTvi_YvRTYnA8OVmkuEHDrVr8cf9Xa9zk2KfXovb4f9vpz6oIcuqjM-EYVfC5PVLYObhVQWW9HZW4Omcewpp-t9M2z7YRZqMAuyeYAsN7_uctScoh6Q634YDSlXiyXnQ81zg3VwVC_C3pWjVnlm8ZLKb5mRAnMDe4li3FXj9OYWlzJu3Ti18TOw2ig2eB0H0D-jdMcMS4Y8CtLOX_IEKQs6f6IXgl6jpo7uDYvKnwQ11zVaX-Bvw8oan79M2.ZwuIdSCc4QYv2imcbp2Tig HTTP/1.1" 302 -
127.0.0.1 - - [26/Nov/2018 10:56:54] "GET /login HTTP/1.1" 302 -
127.0.0.1 - - [26/Nov/2018 10:56:54] "GET /oidc_callback?state=eyJjc3JmX3Rva2VuIjogIlluRDc0UUVLVGhRRkw5TGtuRU9RZGprNTBheVk1cERkIiwgImRlc3RpbmF0aW9uIjogImV5SmhiR2NpT2lKSVV6STFOaUo5LkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk5UQXdNUzlzYjJkcGJpSS50MVVCRUszbFBxSmZRSzkzMHB5UktBNUZibmNtU0h6TElLblgweXgtTElJIn0%3D&session_state=96eb0bd8-a4a3-49a5-a00c-f4d621cd68e0&code=eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..4SU_gWqEUykjTc78z47zYg.TzPRPlLCmJ7Ofzp5wHMwJam4pmc21_qo0p8bIpULbDE8Q39IESxSO2Sxqvxi67xnNXL90CqbG5uRt3k_2oDPzFUCjoNw0EDibiqSPlnuMNgizGSCXAyVV8DafMJqTGhnbHUUpGVqLzMosIlfwM14jhjXFick0GaC10TPFFdiGZdfVFZlSH95XtrGQ-e9dfgpvi5ioPhlQ1S9Eo9kqSh9WwhOCfGRZe9GNLNFtUT9YCPHHmLirRNLc5NiOdm-kH3L.2Mmopk3YJ0_AiCjk2ArKwQ HTTP/1.1" 302 -
...

And after a while of trying I get also this error in the console:

oauth2client.client.FlowExchangeError
oauth2client.client.FlowExchangeError: invalid_grantCode not valid

This is my flask app code:

import json
from flask import Flask, g
from flask_oidc import OpenIDConnect

app = Flask(__name__)

app.config.from_mapping(
        SECRET_KEY='b3d6a4b1-7f8d-4499-a1ae-6faa053d5b67',
        OIDC_CLIENT_SECRETS='./keycloak.json',
        OIDC_VALID_ISSUERS=['http://localhost:8090/auth/realms/myrealm'],
        OIDC_INTROSPECTION_AUTH_METHOD='client_secret_post',
        OIDC_TOKEN_TYPE_HINT='access_token',
    )

oidc = OpenIDConnect(app)


@app.route("/")
def hello():
    if oidc.user_loggedin:
        return 'Welcome %s' % oidc.user_getfield('email')
    else:
        return 'Not logged in'


@app.route('/login')
@oidc.require_login
def login():
    return 'Welcome %s' % oidc.user_getfield('email')


@app.route('/api')
@oidc.accept_token(require_token=True)
def my_api():
    return json.dumps('Welcome %s' % g.oidc_token_info['sub'])

This is my keycloak.json:

{
  "web":
   {
     "client_id": "MyClient",
     "client_secret": "b3d6a4b1-7f8d-4499-a1ae-6faa053d5b67",
     "auth_uri": "http://localhost:8090/auth/realms/myrealm/protocol/openid-connect/auth",
     "token_uri": "http://localhost:8090/auth/realms/myrealm/protocol/openid-connect/token",
     "token_introspection_uri": "http://localhost:8090/auth/realms/myrealm/protocol/openid-connect/token/introspect",
     "realm": "myrealm",
     "ssl-required": "none",
     "resource": "MyClient"
   }
}

In my Keycloak admin console I have set-up my client and as far as I understand, the fact that it shows the keycloak login screen properly is because it's configured as expected, however I cannot make work the app once the login is performed.

enter image description here

I also tried to override the default callback (which I'm not clear about if I have to implement it or not btw):

For that I added this (taken from the docs):

OVERWRITE_REDIRECT_URI='http://localhost:5001/custom_callback'

@app.route('/custom_callback')
@oidc.custom_callback
def callback(data):
    return 'Hello. You submitted %s' % data

And this to my keycloak.json:

     "redirect_uris": [
         "http://localhost:5001/custom_callback"
     ],

But no success in recognizing the logged in user. Though I see a state var in the querystring.. what should I do with that?

What am I missing?

Should I implement a custom callback? in that case can anybody give an example of how to make my flask app aware of the user logged in?

Thanks a lot in advance!

Lashandralashar answered 26/11, 2018 at 9:8 Comment(6)
Do I have to override the callback and manually perform an access token request with the code I get from keycloak in the redirect? if that's the case what's the point of the flask-oidc library? I'd appreciate any helpLashandralashar
Were you able to solve this problem?Shack
No. And after some time of trying without success we decided to go for a different approach.Lashandralashar
Anybody had any success with this? @Lashandralashar can you share the other approach you have used.Maybe
I'm using docker and implemented a GW using nginx, and all the routings go through it, meaning that if there is any service that needs to be authenticated or secured, I manage it there, for the main services I use jtw authentication over a python framework.Lashandralashar
I can not even get the keyclaok login page! @oidc.require_login lands me {"error":"invalid_request","error_description":"Missing parameter: username"} @Lashandralashar can you share your keycloak settings, I don't know what am I doing wrong.Prosaism
Z
1

The problem is the iat check.. This check will allow connection if the issuance time (must be lower that expiry time) is larger that the current time, if not, this check will return false and display error. So to resolve the problem you need to set 'OIDC_CLOCK_SKEW' and create audience scope keycloak side.

Here the part of code :

# step 10: check iat
    if id_token['iat'] < (time.time() -
                          current_app.config['OIDC_CLOCK_SKEW']):
        logger.error('Token issued in the past')
        return False

I don't know if it's the good way or if it's a server/package issue but it works for me. Because it's not possible to have an issuance time >= current_time, right ?

My configuration :

app.config.update({
    'DEBUG': True,
    'TESTING': True,
    'SECRET_KEY': 'testest',
    'OIDC_CLIENT_SECRETS': 'client_secrets.json',
    'OIDC_ID_TOKEN_COOKIE_SECURE': False,
    'OIDC_REQUIRE_VERIFIED_EMAIL': False,
    'OIDC_USER_INFO_ENABLED': True,
    'OIDC_OPENID_REALM': 'fake_realm',
    'OIDC_SCOPES': ['openid', 'email', 'profile'],
    'OIDC_INTROSPECTION_AUTH_METHOD': 'client_secret_post',
    'OIDC_RESOURCE_CHECK_AUD': True, #Audience
    'OIDC_CLOCK_SKEW': 560 #iat must be > time.time() - OIDC_CLOCK_SKEW
}) 

I put that here to help others to resolve it :)

EDIT:

In your case, in addition you also need to add "redirect_uris" to your json like this: (it must be the same uri that keycloak side)

{
    "web": {
        "issuer": "https://{server_name}/auth/realms/fake_realm",
        "auth_uri": "https://{server_name}/auth/realms/fake_realm/protocol/openid-connect/auth",
        "client_id": "fake_realm",
        "client_secret": "ac981e95-f97b-******-*******-*****",
        "redirect_uris": [
            "http://localhost:5000/oidc_callback"
        ],
        "userinfo_uri": "https://{server_name}/auth/realms/fake_realm/protocol/openid-connect/userinfo",
        "token_uri": "https://{server_name}/auth/realms/fake_realm/protocol/openid-connect/token",
        "token_introspection_uri": "https://{server_name}/auth/realms/fake_realm/protocol/openid-connect/token/introspect"
    }
}
Zoellick answered 9/7, 2019 at 11:34 Comment(0)
R
0

I had solved this problem by changing time setting. I had set the time correctly and problem is solved.

Reinforce answered 13/7, 2021 at 5:17 Comment(1)
Could you expand on that?Ivoryivorywhite
C
0

I just had the same problem. The continuous redirects seemed to come from flask_oidc wanting to set an 'oidc_id_token' but my browser was not storing the cookie since it was marked 'Secure', meaning that it will only store if transferred over https and not when using plain http.

To solve this you can update your app.config values as follows:

app.config.update({
    ...
    'OIDC_COOKIE_SECURE': False, # Very important when working with server without ssl
    'OIDC_ID_TOKEN_COOKIE_SECURE': False, # Very important when working with server without ssl
})
Capparidaceous answered 30/11, 2023 at 11:18 Comment(0)
P
0

app.config.update({ ... 'OIDC_COOKIE_SECURE': False, # Very important when working with server without ssl 'OIDC_ID_TOKEN_COOKIE_SECURE': False, # Very important when working with server without ssl })

Pedicular answered 30/11, 2023 at 11:22 Comment(2)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Pergolesi
Hello, please don't post code only and add an explantation as to why you think that this is the optimal solution. People are supposed to learn from your answer, which might not occur if they just copy paste code without knowing why it should be used.Hagiography

© 2022 - 2024 — McMap. All rights reserved.