def test_make_id_token(self): self.srv.keyjar["http://oic.example/rp"] = KC_RSA session = {"sub": "user0", "client_id": "http://oic.example/rp"} issuer = "http://oic.example/idp" code = "abcdefghijklmnop" _idt = self.srv.make_id_token(session, loa="2", issuer=issuer, code=code, access_token="access_token") algo = "RS256" ckey = self.srv.keyjar.get_signing_key(alg2keytype(algo), session["client_id"]) _signed_jwt = _idt.to_jwt(key=ckey, algorithm="RS256") idt = IdToken().from_jwt(_signed_jwt, keyjar=self.srv.keyjar) _jwt = JWT().unpack(_signed_jwt) lha = left_hash(code.encode("utf-8"), func="HS" + _jwt.headers["alg"][-3:]) assert lha == idt["c_hash"] atr = AccessTokenResponse(id_token=_signed_jwt, access_token="access_token", token_type="Bearer") atr["code"] = code assert atr.verify(keyjar=self.srv.keyjar)
def test_token_endpoint(self): authreq = AuthorizationRequest(state="state", redirect_uri="http://example.com/authz", client_id=CLIENT_ID, response_type="code", scope=["openid"]) _sdb = self.provider.sdb sid = _sdb.token.key(user="******", areq=authreq) access_grant = _sdb.token(sid=sid) ae = AuthnEvent("user", "salt") _sdb[sid] = { "oauth_state": "authz", "authn_event": ae, "authzreq": authreq.to_json(), "client_id": CLIENT_ID, "code": access_grant, "code_used": False, "scope": ["openid"], "redirect_uri": "http://example.com/authz", } _sdb.do_sub(sid, "client_salt") # Construct Access token request areq = AccessTokenRequest(code=access_grant, client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=CLIENT_SECRET) txt = areq.to_urlencoded() resp = self.provider.token_endpoint(request=txt) atr = AccessTokenResponse().deserialize(resp.message, "json") assert _eq(atr.keys(), ['token_type', 'id_token', 'access_token', 'scope', 'expires_in', 'refresh_token'])
def test_token_endpoint(): server = provider_init authreq = AuthorizationRequest(state="state", redirect_uri="http://example.com/authz", client_id=CLIENT_ID) _sdb = server.sdb sid = _sdb.token.key(user="******", areq=authreq) access_grant = _sdb.token(sid=sid) _sdb[sid] = { "oauth_state": "authz", "sub": "user_id", "authzreq": "", "client_id": CLIENT_ID, "code": access_grant, "code_used": False, "scope": ["openid"], "redirect_uri": "http://example.com/authz" } # Construct Access token request areq = AccessTokenRequest(code=access_grant, client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=CLIENT_SECRET) txt = areq.to_urlencoded() resp = server.token_endpoint(request=txt) print resp atr = AccessTokenResponse().deserialize(resp.message, "json") print atr.keys() assert _eq(atr.keys(), ['token_type', 'id_token', 'access_token', 'scope', 'expires_in', 'refresh_token'])
def make_refresh_request(self, refresh_token): request_args = { 'grant_type': 'refresh_token', 'refresh_token': refresh_token, } resp = self.app.test_client().post('/token', data=request_args, headers=self.create_basic_auth_header()) assert resp.status_code == 200 token_response = AccessTokenResponse().from_json(resp.data.decode('utf-8')) assert token_response.verify() return token_response
def make_code_exchange_request(self, code): request_args = { 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': TEST_REDIRECT_URI } resp = self.app.test_client().post('/token', data=request_args, headers=self.create_basic_auth_header()) assert resp.status_code == 200 token_response = AccessTokenResponse().from_json(resp.data.decode('utf-8')) assert token_response.verify(key=[self.app.provider.signing_key]) return token_response
def test_clean_response(): atr = AccessTokenResponse(access_token="access_token", token_type="bearer", expires_in=600, refresh_token="refresh", steps=39, stalls="yes") catr = clean_response(atr) atr_keys = atr.keys() catr_keys = catr.keys() assert _eq(atr_keys, ['token_type', 'access_token', 'expires_in', 'refresh_token', 'steps', 'stalls']) assert _eq(catr_keys, ['token_type', 'access_token', 'expires_in', 'refresh_token'])
def test_wrong_alg(self): idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient'} idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) with pytest.raises(WrongSigningAlgorithm): at.verify(key=[key], algs={"sign": "HS512"})
def clean_response(aresp): """ Creates a new instance with only the standard attributes :param aresp: The original AccessTokenResponse :return: An AccessTokenResponse instance """ atr = AccessTokenResponse() for prop in atr.parameters(): try: atr[prop] = aresp[prop] except KeyError: pass return atr
def test_parse_access_token_response_missing_attribute(): at = AccessTokenResponse(access_token="SlAV32hkKG", token_type="Bearer", refresh_token="8xLOxBtZp8", expires_in=3600) atdict = at.to_dict() del atdict["access_token"] atj = json.dumps(atdict) print atj client = Client() ATR = AccessTokenResponse raises(MissingRequiredAttribute, "client.parse_response(ATR, info=atj)") atuec = urllib.urlencode(atdict) raises(MissingRequiredAttribute, "client.parse_response(ATR, info=atuec, sformat='urlencoded')")
def token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"]) elif "grant_type=authorization_code": req = self.parse_token_request(body=data) _info = self.sdb.upgrade_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
def test_make_id_token(): srv = Server(KEYS) session = {"user_id": "user0", "client_id": "http://oic.example/rp"} issuer = "http://oic.example/idp" code = "abcdefghijklmnop" idt_jwt = srv.make_id_token(session, loa="2", issuer=issuer, code=code, access_token="access_token") jwt_keys = srv.keystore.get_keys("ver", owner=None) idt = IdToken().from_jwt(idt_jwt, key=jwt_keys) print idt header = unpack(idt_jwt) lha = left_hash(code, func="HS" + header[0]["alg"][-3:]) assert lha == idt["c_hash"] atr = AccessTokenResponse(id_token=idt_jwt, access_token="access_token", token_type="Bearer") atr["code"] = code assert atr.verify(key=jwt_keys)
def test_userinfo_request(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse(access_token="access_token", token_type="Bearer", expires_in=600, refresh_token="refresh", scope=["openid"]) self.client.parse_response(AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0") self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( state="state0") assert path == "http://example.com/userinfo" assert method == "GET" assert body is None assert h_args == {'headers': {'Authorization': 'Bearer access_token'}}
def test_parse_access_token_response(): client = Client() at = AccessTokenResponse(access_token="SlAV32hkKG", token_type="Bearer", refresh_token="8xLOxBtZp8", expires_in=3600) atj = at.to_json() ATR = AccessTokenResponse atr = client.parse_response(ATR, info=atj) assert _eq(atr.keys(), ['access_token', 'token_type', 'expires_in', 'refresh_token']) uec = at.to_urlencoded() raises(ValueError, 'client.parse_response(ATR, info=uec)') uatr = client.parse_response(ATR, info=uec, sformat="urlencoded") assert _eq(uatr.keys(), ['access_token', 'token_type', 'expires_in', 'refresh_token']) huec = "%s?%s" % ("https://example.com/token", uec) uatr = client.parse_response(ATR, info=huec, sformat="urlencoded") assert _eq(uatr.keys(), ['access_token', 'token_type', 'expires_in', 'refresh_token']) err = ErrorResponse(error="invalid_request", error_description="Something was missing", error_uri="http://example.com/error_message.html") jerr = err.to_json() uerr = err.to_urlencoded() _ = client.parse_response(ATR, info=jerr) _ = client.parse_response(ATR, info=uerr, sformat="urlencoded") raises(Exception, 'client.parse_response(ATR, info=jerr, sformat="urlencoded")') raises(Exception, "client.parse_response(ATR, info=uerr)") raises(Exception, 'client.parse_response(ATR, info=jerr, sformat="focus")')
def test_faulty_idtoken(self): idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient'} idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" _faulty_signed_jwt = ".".join(p) _info = {"access_token": "accessTok", "id_token": _faulty_signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) with pytest.raises(BadSignature): at.verify(key=[key])
def test_token_endpoint(): server = provider_init authreq = AuthorizationRequest(state="state", redirect_uri="http://example.com/authz", client_id=CLIENT_ID) _sdb = server.sdb sid = _sdb.token.key(user="******", areq=authreq) access_grant = _sdb.token(sid=sid) _sdb[sid] = { "oauth_state": "authz", "sub": "user_id", "authzreq": "", "client_id": CLIENT_ID, "code": access_grant, "code_used": False, "scope": ["openid"], "redirect_uri":"http://example.com/authz" } # Construct Access token request areq = AccessTokenRequest(code=access_grant, client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=CLIENT_SECRET) str = areq.to_urlencoded() fil = StringIO.StringIO(buf=str) environ = BASE_ENVIRON.copy() environ["REQUEST_METHOD"] = "POST" environ["CONTENT_LENGTH"] = len(str) environ["wsgi.input"] = fil environ["REMOTE_USER"] = CLIENT_ID resp = server.token_endpoint(environ, start_response) print resp atr = AccessTokenResponse().deserialize(resp[0], "json") print atr.keys() assert _eq(atr.keys(), ['token_type', 'id_token', 'access_token', 'scope', 'expires_in', 'refresh_token'])
def test_userinfo_request_post(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse(access_token="access_token", token_type="bearer", expires_in=600, refresh_token="refresh", scope=["openid"]) self.client.parse_response(AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0") self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( method="POST", state="state0") assert path == "http://example.com/userinfo" assert method == "POST" assert body == "access_token=access_token" assert h_args == {'headers': { 'Content-Type': 'application/x-www-form-urlencoded'}}
def _refresh_access_token_endpoint(self, req, **kwargs): _sdb = self.sdb _log_debug = logger.debug client_info = self.cdb[req["client_id"]] assert req["grant_type"] == "refresh_token" rtoken = req["refresh_token"] _info = _sdb.refresh_token(rtoken) if "openid" in _info["scope"]: userinfo = self.userinfo_in_id_token_claims(_info) _idtoken = self.sign_encrypt_id_token(_info, client_info, req, user_info=userinfo) sid = _sdb.token.get_key(rtoken) _sdb.update(sid, "id_token", _idtoken) _log_debug("_info: %s" % _info) atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) _log_debug("access_token_response: %s" % atr.to_dict()) return Response(atr.to_json(), content="application/json")
def _do_token_refresh(self, request): # type: (Mapping[str, str]) -> oic.oic.message.AccessTokenResponse """ Handles a token request for refreshing an access token (grant_type=refresh_token). :param request: parsed http request parameters :return: a token response containing a new Access Token and possibly a new Refresh Token :raise InvalidTokenRequest: if the token request is invalid """ token_request = RefreshAccessTokenRequest().from_dict(request) try: token_request.verify() except MessageException as e: raise InvalidTokenRequest(str(e), token_request) from e response = AccessTokenResponse() access_token, refresh_token = self.authz_state.use_refresh_token( token_request['refresh_token'], scope=token_request.get('scope')) self._add_access_token_to_response(response, access_token) if refresh_token: response['refresh_token'] = refresh_token return response
def test_token_endpoint(self, context, frontend_config, authn_req): token_lifetime = 60 * 60 * 24 frontend_config["provider"]["access_token_lifetime"] = token_lifetime frontend = self.frontend(frontend_config) user_id = "test_user" self.insert_client_in_client_db(frontend, authn_req["redirect_uri"]) self.insert_user_in_user_db(frontend, user_id) authn_req["response_type"] = "code" authn_resp = frontend.provider.authorize(authn_req, user_id) context.request = AccessTokenRequest( redirect_uri=authn_req["redirect_uri"], code=authn_resp["code"]).to_dict() credentials = "{}:{}".format(CLIENT_ID, CLIENT_SECRET) basic_auth = urlsafe_b64encode( credentials.encode("utf-8")).decode("utf-8") context.request_authorization = "Basic {}".format(basic_auth) response = frontend.token_endpoint(context) parsed = AccessTokenResponse().deserialize(response.message, "json") assert parsed["access_token"] assert parsed["expires_in"] == token_lifetime assert parsed["id_token"]
def test_do_userinfo_request_with_state(): """ Mirrors the first lines in do_userinfo_request""" client = Client(CLIENT_ID, client_authn_method=CLIENT_AUTHN_METHOD) client.grant['foxhound'] = Grant() resp = AccessTokenResponse(access_token="access", token_type="Bearer") _token = Token(resp) client.grant["foxhound"].tokens = [_token] method = "GET" state = "foxhound" scope = "openid" request = "openid" kwargs = { "request": request, "userinfo_endpoint": 'http://example.com/userinfo' } path, body, method, h_args = client.user_info_request( method, state, scope, **kwargs) assert path == 'http://example.com/userinfo' assert h_args == {'headers': {'Authorization': 'Bearer access'}} assert method == 'GET' assert body is None
def __call__(self, request): data = request.body req = self.provider.parse_token_request(body=data) if 'grant_type' not in req: return (400, {}, 'Missing grant_type') if req['grant_type'] == 'authorization_code': authz_code = req['code'] authz_info = self.provider.authz_codes[authz_code] auth_req = authz_info['auth_req'] client_id = auth_req['client_id'] if authz_info['used']: raise Exception('code already used') return (400, {}, 'Invalid authorization code') if authz_info['exp'] < time.time(): raise Exception('code expired') return (400, {}, 'Invalid authorization code') authz_info['used'] = True access_token = { 'value': rndstr(), 'expires_in': self.provider.access_token_lifetime, 'type': 'Bearer' } at_value = access_token['value'] self.provider.access_tokens[at_value] = { 'iat': time.time(), 'exp': time.time() + self.provider.access_token_lifetime, 'sub': 'test-sub', 'client_id': client_id, 'aud': [client_id], 'scope': authz_info['granted_scope'], 'granted_scope': authz_info['granted_scope'], 'token_type': access_token['type'], 'auth_req': auth_req } resp = AccessTokenResponse() resp['access_token'] = at_value resp['token_type'] = access_token['type'] resp['expires_in'] = access_token['expires_in'] resp['refresh_token'] = None args = { 'c_hash': jws.left_hash(authz_code.encode('utf-8'), 'HS256'), 'at_hash': jws.left_hash(at_value.encode('utf-8'), 'HS256'), } id_token = IdToken(iss=self.config['issuer'], sub='test-sub', aud=client_id, iat=time.time(), exp=time.time() + self.provider.id_token_lifetime, **args) if 'nonce' in auth_req: id_token['nonce'] = auth_req['nonce'] resp['id_token'] = id_token.to_jwt([self.provider.signing_key], 'RS256') json_data = resp.to_json() return (200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', }, json_data) return (400, {}, 'Unsupported grant_type')
def _do_code_exchange( self, request, # type: Dict[str, str] extra_id_token_claims=None # type: Optional[Union[Mapping[str, Union[str, List[str]]], Callable[[str, str], Mapping[str, Union[str, List[str]]]]] ): # type: (...) -> oic.message.AccessTokenResponse """ Handles a token request for exchanging an authorization code for an access token (grant_type=authorization_code). :param request: parsed http request parameters :param extra_id_token_claims: any extra parameters to include in the signed ID Token, either as a dict-like object or as a callable object accepting the local user identifier and client identifier which returns any extra claims which might depend on the user id and/or client id. :return: a token response containing a signed ID Token, an Access Token, and a Refresh Token :raise InvalidTokenRequest: if the token request is invalid """ token_request = AccessTokenRequest().from_dict(request) try: token_request.verify() except MessageException as e: raise InvalidTokenRequest(str(e), token_request) from e authentication_request = self.authz_state.get_authorization_request_for_code( token_request['code']) if token_request['client_id'] != authentication_request['client_id']: logger.info( 'Authorization code \'%s\' belonging to \'%s\' was used by \'%s\'', token_request['code'], authentication_request['client_id'], token_request['client_id']) raise InvalidAuthorizationCode('{} unknown'.format( token_request['code'])) if token_request['redirect_uri'] != authentication_request[ 'redirect_uri']: raise InvalidTokenRequest( 'Invalid redirect_uri: {} != {}'.format( token_request['redirect_uri'], authentication_request['redirect_uri']), token_request) sub = self.authz_state.get_subject_identifier_for_code( token_request['code']) user_id = self.authz_state.get_user_id_for_subject_identifier(sub) response = AccessTokenResponse() access_token = self.authz_state.exchange_code_for_token( token_request['code']) self._add_access_token_to_response(response, access_token) refresh_token = self.authz_state.create_refresh_token( access_token.value) if refresh_token is not None: response['refresh_token'] = refresh_token if extra_id_token_claims is None: extra_id_token_claims = {} elif callable(extra_id_token_claims): extra_id_token_claims = extra_id_token_claims( user_id, authentication_request['client_id']) requested_claims = self._get_requested_claims_in( authentication_request, 'id_token') user_claims = self.userinfo.get_claims_for(user_id, requested_claims) response['id_token'] = self._create_signed_id_token( authentication_request['client_id'], sub, user_claims, authentication_request.get('nonce'), None, access_token.value, extra_id_token_claims) logger.debug( 'issued id_token=%s from requested_claims=%s userinfo=%s extra_claims=%s', response['id_token'], requested_claims, user_claims, extra_id_token_claims) return response
cli.scope = ["openid", "profile"] cli.redirect_uris = ["https://client.example.com/cb"] request_args = {"response_type": "code id_token", "state": "af0ifjsldkj"} oidr = cli.construct_AuthorizationRequest(request_args=request_args) print oidr.keys() assert _eq(oidr.keys(), [ 'nonce', 'state', 'redirect_uri', 'response_type', 'client_id', 'scope' ]) ARESP = AuthorizationResponse(code="code", state="state000") TRESP = AccessTokenResponse(access_token="access_token", token_type="bearer", expires_in=600, refresh_token="refresh", scope=["openid"]) def test_userinfo_request(): cli = Client() cli.userinfo_endpoint = "http://example.com/userinfo" info = ARESP.to_urlencoded() cli.parse_response(AuthorizationResponse, info, sformat="urlencoded", state="state0") cli.parse_response(AccessTokenResponse, TRESP.to_json(), state="state0")
def test_token_type(self): # lacks required token_type parameter _info = {"access_token": "accessTok", "id_token": "blabla"} at = AccessTokenResponse(**_info) with pytest.raises(MissingRequiredAttribute): at.verify()
_tinfo = _sdb.update_to_token(_access_code) except Exception, err: logger.error("%s" % err) # Should revoke the token issued to this access code _sdb.revoke_all_tokens(_access_code) return self._error(error="access_denied", descr="%s" % err) if "openid" in _info["scope"]: userinfo = self.userinfo_in_id_token_claims(_info) _idtoken = self.sign_encrypt_id_token(_info, client_info, req, user_info=userinfo) _sdb.update_by_token(_access_code, "id_token", _idtoken) _log_debug("_tinfo: %s" % _tinfo) atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo)) _log_debug("access_token_response: %s" % atr.to_dict()) return Response(atr.to_json(), content="application/json") def _refresh_access_token_endpoint(self, req, **kwargs): _sdb = self.sdb _log_debug = logger.debug client_info = self.cdb[req["client_id"]] assert req["grant_type"] == "refresh_token" rtoken = req["refresh_token"] _info = _sdb.refresh_token(rtoken)
def test_full_flow(self, context, frontend): redirect_uri = "https://client.example.com/redirect" response_type = "code id_token token" mock_callback = Mock() frontend.auth_req_callback_func = mock_callback # discovery http_response = frontend.provider_config(context) provider_config = ProviderConfigurationResponse().deserialize( http_response.message, "json") # client registration registration_request = RegistrationRequest( redirect_uris=[redirect_uri], response_types=[response_type]) context.request = registration_request.to_dict() http_response = frontend.client_registration(context) registration_response = RegistrationResponse().deserialize( http_response.message, "json") # authentication request authn_req = AuthorizationRequest( redirect_uri=redirect_uri, client_id=registration_response["client_id"], response_type=response_type, scope="openid email", state="state", nonce="nonce") context.request = dict(parse_qsl(authn_req.to_urlencoded())) frontend.handle_authn_request(context) assert mock_callback.call_count == 1 # fake authentication response from backend internal_response = self.setup_for_authn_response( context, frontend, authn_req) http_response = frontend.handle_authn_response(context, internal_response) authn_resp = AuthorizationResponse().deserialize( urlparse(http_response.message).fragment, "urlencoded") assert "code" in authn_resp assert "access_token" in authn_resp assert "id_token" in authn_resp # token request context.request = AccessTokenRequest( redirect_uri=authn_req["redirect_uri"], code=authn_resp["code"]).to_dict() credentials = "{}:{}".format(registration_response["client_id"], registration_response["client_secret"]) basic_auth = urlsafe_b64encode( credentials.encode("utf-8")).decode("utf-8") context.request_authorization = "Basic {}".format(basic_auth) http_response = frontend.token_endpoint(context) parsed = AccessTokenResponse().deserialize(http_response.message, "json") assert "access_token" in parsed assert "id_token" in parsed # userinfo request context.request = {} context.request_authorization = "Bearer {}".format( parsed["access_token"]) http_response = frontend.userinfo_endpoint(context) parsed = OpenIDSchema().deserialize(http_response.message, "json") assert "email" in parsed
_sdb.revoke_all_tokens(_access_code) return self._error(environ, start_response, error="access_denied", descr= "%s" % err) if "openid" in _info["scope"]: try: _idtoken = self._id_token(_info) except AccessDenied: return self._error(environ, start_response, error="access_denied") _sdb.update_by_token(_access_code, "id_token", _idtoken) _log_debug("_tinfo: %s" % _tinfo) atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo)) if self.test_mode: _log_info("access_token_response: %s" % atr.to_dict()) resp = Response(atr.to_json(), content="application/json") return resp(environ, start_response) def _bearer_auth(self, environ): #'HTTP_AUTHORIZATION': 'Bearer pC7efiVgbI8UASlolltdh76DrTZ2BQJQXFhVvwWlKekFvWCcdMTmNCI/BCSCxQiG' try: authn = environ["HTTP_AUTHORIZATION"] try: assert authn[:6].lower() == "bearer" _token = authn[7:] except AssertionError:
def __call__(self, request): data = request.body req = self.provider.parse_token_request(body=data) if 'grant_type' not in req: return (400, {}, 'Missing grant_type') if req['grant_type'] == 'authorization_code': authz_code = req['code'] authz_info = self.provider.authz_codes[authz_code] auth_req = authz_info['auth_req'] client_id = auth_req['client_id'] if authz_info['used']: raise Exception('code already used') return (400, {}, 'Invalid authorization code') if authz_info['exp'] < time.time(): raise Exception('code expired') return (400, {}, 'Invalid authorization code') authz_info['used'] = True access_token = { 'value': rndstr(), 'expires_in': self.provider.access_token_lifetime, 'type': 'Bearer' } at_value = access_token['value'] self.provider.access_tokens[at_value] = { 'iat': time.time(), 'exp': time.time() + self.provider.access_token_lifetime, 'sub': 'test-sub', 'client_id': client_id, 'aud': [client_id], 'scope': authz_info['granted_scope'], 'granted_scope': authz_info['granted_scope'], 'token_type': access_token['type'], 'auth_req': auth_req } resp = AccessTokenResponse() resp['access_token'] = at_value resp['token_type'] = access_token['type'] resp['expires_in'] = access_token['expires_in'] resp['refresh_token'] = None args = { 'c_hash': jws.left_hash(authz_code.encode('utf-8'), 'HS256'), 'at_hash': jws.left_hash(at_value.encode('utf-8'), 'HS256'), } id_token = IdToken( iss=self.config['issuer'], sub='test-sub', aud=client_id, iat=time.time(), exp=time.time() + self.provider.id_token_lifetime, **args) if 'nonce' in auth_req: id_token['nonce'] = auth_req['nonce'] resp['id_token'] = id_token.to_jwt( [self.provider.signing_key], 'RS256') json_data = resp.to_json() return ( 200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', }, json_data ) return (400, {}, 'Unsupported grant_type')