def authenticate(cls, username=None, password=None): """ Return 2tuple, representing authorized user or (None,None) on fail First tuple element is String ID representing a distingushed name for external users, e.g.: "[email protected],ou=People,o=warehouse-external" Second tuple element is Dict, representing a decoded ODIC access token >>> b = KeycloakBackend() >>> b.authenticate() (None, None) >>> # Check username >>> b.authenticate("*****@*****.**", "anything") (None, None) >>> # Check bogus username >>> b.authenticate("Test", "anything") (None, None) """ user_identifier = None decoded_token = None if username is not None: u = None #auth may have failed: prepare to return None # setup OIDC client conf = config_loader.get_api_config() auth_url = conf['external.keycloak.url'] dw_client_id = conf['external.keycloak.warehouse_client_id'] auth_client = KeycloakOpenID(server_url=auth_url ,client_id=dw_client_id, realm_name="master" ,client_secret_key="value-doesnt-matter" ,verify=False) try: # login encoded_token = auth_client.token(username, password) # get server public key iss_url = auth_url+'realms/master' json_iss = requests.get(iss_url, verify=False).json() pub_key = add_pem_headfoot(json_iss['public_key']) # decode server response decode_opts = {"verify_signature":True, "verify_aud": True, "exp": True} decoded_token = auth_client.decode_token( encoded_token['access_token'] ,key=pub_key, options=decode_opts) user_dn = oidc_token_to_ldap_style_name(decoded_token) #login is valid user_identifier = user_dn #return except KeycloakAuthenticationError: pass #login is invalid return user_identifier, decoded_token
def load_user_from_request(request): if request.path != "/" and not app.config.get('is_local') and os.environ.get("TESTING") is None: try: api_key = request.headers.get('Authorization') bearer_token = api_key.replace('Bearer ', '', 1) keycloak_openid = KeycloakOpenID( server_url=app.config.get('authentication', {}).get('url'), client_id=app.config.get('authentication', {}).get('clientid'), realm_name=app.config.get('authentication', {}).get('realm') ) keycloak_public_key = "-----BEGIN PUBLIC KEY-----\n" + keycloak_openid.public_key() + "\n-----END PUBLIC KEY-----" options = {"verify_signature": True, "verify_aud": True, "verify_exp": True} token_info = keycloak_openid.decode_token(bearer_token, key=keycloak_public_key, options=options) return Auth.authenticate_user(token_info, app.config.get('authentication')) except Exception as e: logging.exception(e) return make_response("Error occured while authentication: ", str(e), 500) else: return User(is_authenticated=True)
def validate(token): KEYCLOAK_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvgTcVSXga3C6Tam96Q2kmnQ7U55f0hA/56HRQnsY9V0d21QlLkkD+DvIbKGaDVNLp8I+Gqlx4s6u5oDKIA12BbE9jAWz7nblKJUMjESDbpQJVivLAOXPvHQK8tCvxClp8dLEb9cEGXbabO+YIWN3pNn5CL8l3IBbJs1sT76G3jyo0sw5oiSRj0agxIPrtThDPDKhOqG0V+NYtudeE1FhWzt4ajYS4yIHGioVtYsvTfePMRGLRHTMQPjia8kBwnVRDwqMputLGfBg8qnz/xUPiRj5hmEeLuT73zte/CLmfSyqn7IsOrso1zEXgBBLz8XUsp1elGRz+zZtkHqnPot8EQIDAQAB" KEYCLOAK_PUBLIC_KEY = f"-----BEGIN PUBLIC KEY-----\n{KEYCLOAK_PUBLIC_KEY}\n-----END PUBLIC KEY-----" options = {"verify_signature": True, "exp": True} keycloak_openid = KeycloakOpenID( server_url="http://localhost:8080/auth/", client_id="api", realm_name="master", client_secret_key="05e8bfbf-e050-4830-bc8a-d5f4376b13ff") try: token_info = keycloak_openid.decode_token(token, KEYCLOAK_PUBLIC_KEY, options=options) if (token_info): return True except jwt.JWTClaimsError: return False except jwt.ExpiredSignatureError: return False except jwt.JWSError: return False
class _Keycloak(AuthProvider): f""" Redis key-value pair database implementation of KeyValueStore Defines methods for getting, deleting and putting key value pairs :param host, port, db (see also `https://github.com/andymccurdy/redis-py)` Example: ``` db = KeyValueDatabase.instance(provider='redis', host='localhost', port=6379, db=0) ``` """ def __init__(self, domain: Optional[str] = None, audience: Optional[str] = None): super().__init__() self._domain = domain or os.getenv('KEYCLOAK_DOMAIN') self._client_secret_key = os.getenv('KEYCLOAK_CLIENT_SECRET_KEY') self._client_secret_id = os.getenv('KEYCLOAK_CLIENT_SECRET_ID') self._realm = os.getenv('KEYCLOAK_REALM') self._audience = audience or os.getenv('XCUBE_HUB_OAUTH_AUD') self._algorithms = ["RS256"] if self._domain is None: raise Unauthorized(description="Keycloak error: Domain not set") if self._audience is None: raise Unauthorized(description="Keycloak error: Audience not set") self._keycloak_openid = KeycloakOpenID( server_url=f"https://{self._domain}/auth/", client_id=self._client_secret_id, realm_name=self._realm, client_secret_key=self._client_secret_key, verify=True) def get_email(self, claims): if 'email' not in claims: return "no email" return claims['email'] def verify_token(self, token: str) -> Dict: """ Get a key value :param token: :return: """ try: self._keycloak_openid.introspect(token) except KeycloakGetError as e: raise Unauthorized(description="invalid token: " + str(e)) certs = self._keycloak_openid.certs() rsa_key = {} for key in certs["keys"]: rsa_key = { "kty": key["kty"], "kid": key["kid"], "use": key["use"], "n": key["n"], "e": key["e"] } try: self._claims = self._keycloak_openid.decode_token(token=token, key=rsa_key) except Exception as e: raise Unauthorized(description=str(e)) return self._claims
def check_password(self, user_id, password): """ Attempt to authenticate a user against an Keycloak Server and register an account if none exists. Returns: True if authentication against Keycloak was successful """ if not password: defer.returnValue(False) localpart = user_id.split(":", 1)[0][1:] logger.info("! %s", localpart) keycloak_openid = KeycloakOpenID(server_url=self.url, client_id=self.client_id, realm_name=self.realm_name, client_secret_key=self.secret_key) logger.debug("Attempting Keycloak connection with %s", self.url) try: token = yield keycloak_openid.token(localpart, password) except KeycloakAuthenticationError as e: logger.info("Failed login attempt %s error: %s", localpart, e) defer.returnValue(False) logger.info("User %s authenticated", user_id) options = {"verify_signature": True, "verify_aud": True, "exp": True} key = self.public_key if self.algorithm == 'RS256': key = '-----BEGIN PUBLIC KEY-----\n' + key + '\n-----END PUBLIC KEY-----' token_info = keycloak_openid.decode_token(token['access_token'], key=key, algorithms=[self.algorithm], options=options) if not (yield self.account_handler.check_user_exists(user_id)): logger.info("User %s does not exist yet, creating...", user_id) if localpart != localpart.lower() and self.regLower: logger.info( 'User %s was cannot be created due to username lowercase policy', localpart) defer.returnValue(False) user_id, access_token = (yield self.account_handler.register( localpart=localpart)) registration = True logger.info( "Registration based on REST data was successful for %s", user_id) else: logger.info("User %s already exists, registration skipped", user_id) if bool(self.profile_attrs): logger.info("profile attrs") store = yield self.account_handler.hs.get_profile_handler().store profile = {} for key, alias in self.profile_attrs.items(): if alias in token_info: profile[key] = token_info[alias] display_name = profile.pop('display_name', None) if display_name: logger.info( "Setting display name to '%s' based on profile data", display_name) yield store.set_profile_displayname(localpart, display_name) logger.info("end profile attrs") # TODO 3pids else: logger.info("No profile data") def _save_keycloak_token(txn): template = """ INSERT INTO keycloak_provider_tokens (user_id, refresh_tokens) VALUES ('{0}', ARRAY['{1}']) ON CONFLICT (user_id) DO UPDATE SET refresh_tokens = array_append(keycloak_provider_tokens.refresh_tokens, '{1}') """ sql = template.format(user_id, token['refresh_token']) txn.execute(sql) self.account_handler.run_db_interaction("save_keycloak_token", _save_keycloak_token) logger.info("insert end") defer.returnValue(True)
# Get RPT (Entitlement) token = keycloak_openid.token("user", "password") rpt = keycloak_openid.entitlement(token['access_token'], "resource_id") # Instropect RPT token_rpt_info = keycloak_openid.introspect(keycloak_openid.introspect(token['access_token'], rpt=rpt['rpt'], token_type_hint="requesting_party_token")) # Introspect Token token_info = keycloak_openid.introspect(token['access_token'])) # Decode Token KEYCLOAK_PUBLIC_KEY = keycloak_openid.public_key() options = {"verify_signature": True, "verify_aud": True, "exp": True} token_info = keycloak_openid.decode_token(token['access_token'], key=KEYCLOAK_PUBLIC_KEY, options=options) # Get permissions by token token = keycloak_openid.token("user", "password") keycloak_openid.load_authorization_config("example-authz-config.json") policies = keycloak_openid.get_policies(token['access_token'], method_token_info='decode', key=KEYCLOAK_PUBLIC_KEY) permissions = keycloak_openid.get_permissions(token['access_token'], method_token_info='introspect') # KEYCLOAK ADMIN from keycloak import KeycloakAdmin keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", username='******', password='******', realm_name="example_realm",