Netflix / lemur

Repository for the Lemur Certificate Manager
Apache License 2.0
1.73k stars 322 forks source link

Lemur integration with OKTA for SSO(oauth2) #3705

Open Syesad opened 3 years ago

Syesad commented 3 years ago

Hi Team,

I am trying to integrate Lemur with OKTA, configuring OKTA as oauth provider as below.

Authentication Providers

ACTIVE_PROVIDERS = ["oauth2"] OAUTH2_SECRET = '[omitted]' OAUTH2_ACCESS_TOKEN_URL = "https://dev-51721667.okta.com/oauth2/v1/authorize" OAUTH2_USER_API_URL = "https://dev-51721667.okta.com/oauth2/v1/userinfo" OAUTH2_JWKS_URL = "https://dev-51721667.okta.com/oauth2/v1/keys" OAUTH2_NAME = "OKTA Oauth2 Provider" OAUTH2_CLIENT_ID = "[omitted]" OAUTH2_REDIRECT_URI = "http://ec2-52-32-140-70.us-west-2.compute.amazonaws.com/api/1/auth/oauth2" OAUTH2_AUTH_ENDPOINT = "https://dev-51721667.okta.com/oauth2/oauth2/v1/authorize" OAUTH2_VERIFY_CERT = True OAUTH_STATE_TOKEN_SECRET = '[omitted]' OAUTH_STATE_TOKEN_STALE_TOLERANCE_SECONDS = 15

This is the error that I get . [2021-08-02 22:58:22,113] ERROR in views: OAuth State Token Secret must be bytes-like. OAuth State Token Secret must be bytes-like. OAuth State Token Secret must be bytes-like. [2021-08-02 22:58:22,114] ERROR in app: Exception on /api/1/auth/providers [GET] Traceback (most recent call last): File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request return self.view_functionsrule.endpoint File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 467, in wrapper resp = resource(*args, kwargs) File "/www/lemur/lib/python3.8/site-packages/flask/views.py", line 89, in view return self.dispatch_request(*args, *kwargs) File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 582, in dispatch_request resp = meth(args, kwargs) File "/www/lemur/lemur/auth/views.py", line 686, in get "state": generate_state_token(), File "/www/lemur/lemur/auth/views.py", line 305, in generate_state_token h.update(ts) AttributeError: 'NoneType' object has no attribute 'update' Exception on /api/1/auth/providers [GET] Traceback (most recent call last): File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request return self.view_functionsrule.endpoint File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 467, in wrapper resp = resource(*args, kwargs) File "/www/lemur/lib/python3.8/site-packages/flask/views.py", line 89, in view return self.dispatch_request(*args, *kwargs) File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 582, in dispatch_request resp = meth(args, kwargs) File "/www/lemur/lemur/auth/views.py", line 686, in get "state": generate_state_token(), File "/www/lemur/lemur/auth/views.py", line 305, in generate_state_token h.update(ts) AttributeError: 'NoneType' object has no attribute 'update' Exception on /api/1/auth/providers [GET] Traceback (most recent call last): File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request return self.view_functionsrule.endpoint File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 467, in wrapper resp = resource(*args, kwargs) File "/www/lemur/lib/python3.8/site-packages/flask/views.py", line 89, in view return self.dispatch_request(*args, *kwargs) File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 582, in dispatch_request resp = meth(args, kwargs) File "/www/lemur/lemur/auth/views.py", line 686, in get "state": generate_state_token(), File "/www/lemur/lemur/auth/views.py", line 305, in generate_state_token h.update(ts) AttributeError: 'NoneType' object has no attribute 'update'

havron commented 3 years ago

Hi @Syesad,

Could you add a literal b in front of your OAUTH_STATE_TOKEN_SECRET in config?

OAUTH_STATE_TOKEN_SECRET = b'<mysecret>'

It needs to be bytes-like; it should be generated that way by default. Also, after testing this, I'd recommend rotating the secrets shown above -- given that EC2 instance is publicly accessible.

Syesad commented 3 years ago

Thank you for taking time and responding to this Sam! I did make that change, and I tried couple of other things, but it is not working and gives me a different error. Here are the details.

Lemur Config:(Secrets are changed)

ACTIVE_PROVIDERS = ["oauth2"] OAUTH2_SECRET = '[omitted]' OAUTH2_ACCESS_TOKEN_URL = "https://dev-51721667.okta.com/oauth2/v1/token" OAUTH2_USER_API_URL = "https://dev-51721667.okta.com/oauth2/v1/userinfo" OAUTH2_JWKS_URL = "https://dev-51721667.okta.com/oauth2/v1/keys" OAUTH2_NAME = "OKTA Oauth2 Provider" OAUTH2_CLIENT_ID = "[omitted]" OAUTH2_REDIRECT_URI = "http://ec2-52-32-140-70.us-west-2.compute.amazonaws.com/api/1/auth/oauth2" OAUTH2_AUTH_ENDPOINT = "https://dev-51721667.okta.com/oauth2/v1/authorize" OAUTH2_VERIFY_CERT = False OAUTH_STATE_TOKEN_SECRET = b'[omitted]'

OAUTH_STATE_TOKEN_SECRET = lemur.common.utils.get_state_token_secret()

OAUTH_STATE_TOKEN_STALE_TOLERANCE_SECONDS = 15

OKTA Config: Sign-in redirect : http://ec2-52-32-140-70.us-west-2.compute.amazonaws.com/api/1/auth/oauth2 sign-out redirect :http://ec2-52-32-140-70.us-west-2.compute.amazonaws.com(this will be changed later)

The error is

Http bad request : 400 dev-51721667.okta.com/oauth2/v1/authorize?response_type=code&client_id=0oadfadfakjhrjkjwq&redirect_uri=http://ec2-52-32-140-70.us-west-2.compute.amazonaws.com/api/1/auth/oauth2&scope=openid%20email%20profile%20groups&state=6109a7ac%3AUnvI6Xb9eLRGDAbG%2BuObnuigQT9T3Ew3YSOAoEKueVw%3D&nonce=QWZEIF~$+(~^tqpuks570361

logs in lemur:

KeyError: 'id_token' Exception on /api/1/auth/oauth2 [POST] Traceback (most recent call last): File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request return self.view_functionsrule.endpoint File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 467, in wrapper resp = resource(*args, kwargs) File "/www/lemur/lib/python3.8/site-packages/flask/views.py", line 89, in view return self.dispatch_request(*args, *kwargs) File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 582, in dispatch_request resp = meth(args, kwargs) File "/www/lemur/lemur/auth/views.py", line 548, in post id_token, access_token = exchange_for_access_token( File "/www/lemur/lemur/auth/views.py", line 80, in exchange_for_access_token id_token = r.json()["id_token"] KeyError: 'id_token' Exception on /api/1/auth/oauth2 [POST] Traceback (most recent call last): File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request return self.view_functionsrule.endpoint File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 467, in wrapper resp = resource(*args, kwargs) File "/www/lemur/lib/python3.8/site-packages/flask/views.py", line 89, in view return self.dispatch_request(*args, *kwargs) File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 582, in dispatch_request resp = meth(args, kwargs) File "/www/lemur/lemur/auth/views.py", line 548, in post id_token, access_token = exchange_for_access_token( File "/www/lemur/lemur/auth/views.py", line 80, in exchange_for_access_token id_token = r.json()["id_token"] KeyError: 'id_token'

havron commented 3 years ago

Hi @Syesad cool, looks like the state token secret is working. For this new bad request error during token exchange, I have a guess that might merit a PR:

https://github.com/Netflix/lemur/blob/26601920820e6138fe78b7d5fc9af18019af5e2b/lemur/auth/views.py#L69

In this line above, could you try capitalizing the lowercase "authorization": "basic {0}" to "Authorization": "Basic {0}"? It's possible that Okta implemented the OAuth2 RFC in a case-sensitive way.

Syesad commented 3 years ago

Hey Sam! Thank you again for your suggestions. I made that change, with lower case and with upper case, the behavior is same. Lemur is able to talk to OKTA, and able to get the authorization token, but when it is going back to OKTA with Auth Code for getting the access token, OKTA is not accepting that request - It says failure, multiple client credentials.

In Okta forum - they say that the client library is passing client credentials in both the headers and post body. OKTA expects it to be in either header or body. Is there any change that I can do on the lemur side to send the credentials in the request body?

Thanks for your help again

https://devforum.okta.com/t/oidc-token-request-results-in-failure-multiple-client-credentials/207

Syesad commented 3 years ago

Update : I am able to get the access token now. It looks like OKTA doesn't accept the credentials both in header and body. I commented out the client and secret in the body. and then it OKTA started issuing the token. But, now the problem is different.

Change made to get the token from OKTA: params = { "grant_type": "authorization_code", "scope": "openid", "code": code, "redirect_uri": redirect_uri,

"client_id": client_id,

   # "secret" : secret,
}

Different error now: [2021-08-06 01:43:38,914] ERROR in app: Exception on /api/1/auth/oauth2 [POST] Traceback (most recent call last): File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/www/lemur/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request return self.view_functionsrule.endpoint File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 467, in wrapper resp = resource(*args, kwargs) File "/www/lemur/lib/python3.8/site-packages/flask/views.py", line 89, in view return self.dispatch_request(*args, *kwargs) File "/www/lemur/lib/python3.8/site-packages/flask_restful/init.py", line 582, in dispatch_request resp = meth(args, kwargs) File "/www/lemur/lemur/auth/views.py", line 568, in post user, profile = retrieve_user(user_api_url, access_token) File "/www/lemur/lemur/auth/views.py", line 148, in retrieve_user profile = r.json() File "/www/lemur/lib/python3.8/site-packages/requests/models.py", line 900, in json return complexjson.loads(self.text, **kwargs) File "/usr/lib/python3.8/json/init.py", line 357, in loads return _default_decoder.decode(s) File "/usr/lib/python3.8/json/decoder.py", line 337, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/usr/lib/python3.8/json/decoder.py", line 355, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) Exception on /api/1/auth/oauth2 [POST]

havron commented 3 years ago

Hmm, maybe it wants you to set

https://github.com/Netflix/lemur/blob/f82741144b44f18066baa9a152ef32a7953e9bbc/lemur/auth/views.py#L134 ? You're so close to the end of the oauth flow!

rvaneerdewijk commented 3 years ago

@Syesad check my Azure thread. Maybe something I did there will help you.

Thanks for @havron for the input!

jtschladen commented 3 years ago

Hello @Syesad, I just wanted to let you know that I've updated your comments to omit the secret and client ID values from both comments. While I hope that they were just placeholders and not real secrets, I wanted to ensure that no real secrets even appeared to be exposed. Please feel free to scrub any other sensitive information if you have any concerns. Thank you!