def create_digital_user_code(): base = '0123456789' return '-'.join([ generate_token(3, base), generate_token(3, base), generate_token(3, base), ])
def create_bearer_token_generator(self, app): """Create a generator function for generating ``token`` value. This method will create a Bearer Token generator with :class:`authlib.specs.rfc6750.BearerToken`. By default, it will not generate ``refresh_token``, which can be turn on by configuration ``OAUTH2_REFRESH_TOKEN_GENERATOR=True``. """ access_token_generator = app.config.get( 'OAUTH2_ACCESS_TOKEN_GENERATOR', True) if isinstance(access_token_generator, str): access_token_generator = import_string(access_token_generator) else: access_token_generator = lambda: generate_token(42) refresh_token_generator = app.config.get( 'OAUTH2_REFRESH_TOKEN_GENERATOR', False) if isinstance(refresh_token_generator, str): refresh_token_generator = import_string(refresh_token_generator) elif refresh_token_generator is True: refresh_token_generator = lambda: generate_token(48) else: refresh_token_generator = None expires_generator = self.create_expires_generator(app) return BearerToken(access_token_generator, refresh_token_generator, expires_generator)
def _create_oauth2_authorization_url(client, authorization_endpoint, **kwargs): rv = {} if client.code_challenge_method: code_verifier = kwargs.get('code_verifier') if not code_verifier: code_verifier = generate_token(48) kwargs['code_verifier'] = code_verifier rv['code_verifier'] = code_verifier log.debug('Using code_verifier: {!r}'.format(code_verifier)) scope = kwargs.get('scope', client.scope) if scope and scope.startswith('openid'): # this is an OpenID Connect service nonce = kwargs.get('nonce') if not nonce: nonce = generate_token(20) kwargs['nonce'] = nonce rv['nonce'] = nonce url, state = client.create_authorization_url(authorization_endpoint, **kwargs) rv['url'] = url rv['state'] = state return rv
async def handle_redirect_request( self, request: SynapseRequest, client_redirect_url: bytes ) -> None: """Handle an incoming request to /login/sso/redirect It redirects the browser to the authorization endpoint with a few parameters: - ``client_id``: the client ID set in ``oidc_config.client_id`` - ``response_type``: ``code`` - ``redirect_uri``: the callback URL ; ``{base url}/_synapse/oidc/callback`` - ``scope``: the list of scopes set in ``oidc_config.scopes`` - ``state``: a random string - ``nonce``: a random string In addition to redirecting the client, we are setting a cookie with a signed macaroon token containing the state, the nonce and the client_redirect_url params. Those are then checked when the client comes back from the provider. Args: request: the incoming request from the browser. We'll respond to it with a redirect and a cookie. client_redirect_url: the URL that we should redirect the client to when everything is done """ state = generate_token() nonce = generate_token() cookie = self._generate_oidc_session_token( state=state, nonce=nonce, client_redirect_url=client_redirect_url.decode(), ) request.addCookie( SESSION_COOKIE_NAME, cookie, path="/_synapse/oidc", max_age="3600", httpOnly=True, sameSite="lax", ) metadata = await self.load_metadata() authorization_endpoint = metadata.get("authorization_endpoint") uri = prepare_grant_uri( authorization_endpoint, client_id=self._client_auth.client_id, response_type="code", redirect_uri=self._callback_url, scope=self._scopes, state=state, nonce=nonce, ) request.redirect(uri) finish_request(request)
async def create_client_post(request: web.Request): user_id = await aiohttp_security.check_authorized(request) form = await request.post() app = db.App(**form) app.user_id = user_id app.client_id = generate_token(24) # type: ignore app.client_secret = generate_token(48) # type: ignore app.token_endpoint_auth_method = 'client_secret_post' # type: ignore db_session = db.Session() db_session.add(app) db_session.commit() raise web.HTTPFound(location=request.app.router['auth'].url_for())
def submitNewSession(self, pkce=True): """Submit new authorization session :param bool pkce: use PKCE :return: S_OK(str)/S_ERROR() """ session = {} params = dict(state=generate_token(10)) # Create PKCE verifier if pkce: session["code_verifier"] = generate_token(48) params["code_challenge_method"] = "S256" params["code_challenge"] = create_s256_code_challenge(session["code_verifier"]) url, state = self.create_authorization_url(self.get_metadata("authorization_endpoint"), **params) return url, state, session
def create_authorization_url(self, url, state=None, code_verifier=None, **kwargs): """Generate an authorization URL and state. :param url: Authorization endpoint url, must be HTTPS. :param state: An optional state string for CSRF protection. If not given it will be generated for you. :param code_verifier: An optional code_verifier for code challenge. :param kwargs: Extra parameters to include. :return: authorization_url, state """ if state is None: state = generate_token() response_type = self.metadata.get('response_type', 'code') response_type = kwargs.pop('response_type', response_type) if 'redirect_uri' not in kwargs: kwargs['redirect_uri'] = self.redirect_uri if 'scope' not in kwargs: kwargs['scope'] = self.scope if code_verifier and response_type == 'code' and self.code_challenge_method == 'S256': kwargs['code_challenge'] = create_s256_code_challenge(code_verifier) kwargs['code_challenge_method'] = self.code_challenge_method for k in self.EXTRA_AUTHORIZE_PARAMS: if k not in kwargs and k in self.metadata: kwargs[k] = self.metadata[k] uri = prepare_grant_uri( url, client_id=self.client_id, response_type=response_type, state=state, **kwargs) return uri, state
def create_authorization_url(self, url, state=None, **kwargs): """Generate an authorization URL and state. :param url: Authorization endpoint url, must be HTTPS. :param state: An optional state string for CSRF protection. If not given it will be generated for you. :param kwargs: Extra parameters to include. :return: authorization_url, state """ state = state or self.state if state is None: state = generate_token() response_type = self._kwargs.get('response_type', 'code') response_type = kwargs.pop('response_type', response_type) if 'redirect_uri' not in kwargs: kwargs['redirect_uri'] = self.redirect_uri if 'scope' not in kwargs: kwargs['scope'] = self.scope for k in self.EXTRA_AUTHORIZE_PARAMS: if k not in kwargs and k in self._kwargs: kwargs[k] = self._kwargs[k] uri = prepare_grant_uri( url, client_id=self.client_id, response_type=response_type, state=state, **kwargs) return uri, state
def test_pkce_disabled(dummy_application, test_client, dummy_user, pkce_enabled): dummy_application.allow_pkce_flow = pkce_enabled oauth_client = OAuth2Client(MockSession(test_client), dummy_application.client_id, None, code_challenge_method='S256', token_endpoint=url_for('oauth.oauth_token', _external=True), redirect_uri=dummy_application.default_redirect_uri) with test_client.session_transaction() as sess: sess.set_session_user(dummy_user) code_verifier = generate_token(64) auth_endpoint = url_for('oauth.oauth_authorize', _external=True) auth_url = oauth_client.create_authorization_url(auth_endpoint, scope='read:legacy_api', code_verifier=code_verifier)[0] authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) assert authorized_resp.status_code == 302 target_url = authorized_resp.headers['Location'] if pkce_enabled: token = oauth_client.fetch_token(authorization_response=target_url, code_verifier=code_verifier) assert token.keys() == {'access_token', 'token_type', 'scope'} else: with pytest.raises(ValueError) as exc_info: oauth_client.fetch_token(authorization_response=target_url, code_verifier=code_verifier) assert 'invalid_client' in str(exc_info.value)
def test_oauth_flows(create_application, test_client, dummy_user, app, trusted, token_endpoint_auth_method, pkce): oauth_app = create_application(name='test', is_trusted=trusted) oauth_client = OAuth2Client( MockSession(test_client), oauth_app.client_id, oauth_app.client_secret if not pkce else None, code_challenge_method=('S256' if pkce else None), scope='read:user', response_type='code', token_endpoint=url_for('oauth.oauth_token', _external=True), token_endpoint_auth_method=token_endpoint_auth_method, redirect_uri=oauth_app.default_redirect_uri) code_verifier = generate_token(64) if pkce else None auth_url, state = oauth_client.create_authorization_url( url_for('oauth.oauth_authorize', _external=True), code_verifier=code_verifier) with test_client.session_transaction() as sess: sess.set_session_user(dummy_user) # get consent page resp = test_client.get(auth_url) if trusted: authorized_resp = resp else: assert resp.status_code == 200 assert b'is requesting the following permissions' in resp.data assert b'User information (read only)' in resp.data # give consent authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) assert authorized_resp.status_code == 302 target_url = authorized_resp.headers['Location'] target_url_parts = url_parse(target_url) assert f'state={state}' in target_url_parts.query assert target_url == f'{oauth_app.default_redirect_uri}?{target_url_parts.query}' # get a token and make sure it looks fine token = oauth_client.fetch_token(authorization_response=target_url, code_verifier=code_verifier) assert token == { 'access_token': token['access_token'], 'token_type': 'Bearer', 'scope': 'read:user' } with app.test_client() as test_client_no_session: # make sure we can use our token uri, headers, data = oauth_client.token_auth.prepare( '/api/user/', {}, '') resp = test_client_no_session.get(uri, data=data, headers=headers) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id # authorizing again won't require new consent, regardless of the app being trusted assert test_client.get(auth_url).status_code == 302
def create_authorization_code(client, grant_user, request): """ Create an ``AuthorizationCode`` model for the current OAuth request from the given client and user. Certain parameters in the ``AuthorizationCode`` are filled out using the arguments passed from the OAuth request (the redirect URI, scope, and nonce). """ # requested lifetime (in seconds) for the refresh token refresh_token_expires_in = get_valid_expiration_from_request( expiry_param="refresh_token_expires_in", max_limit=config["REFRESH_TOKEN_EXPIRES_IN"], default=config["REFRESH_TOKEN_EXPIRES_IN"], ) code = AuthorizationCode( code=generate_token(50), client_id=client.client_id, redirect_uri=request.redirect_uri, scope=request.scope, user_id=grant_user.id, nonce=request.data.get("nonce"), refresh_token_expires_in=refresh_token_expires_in, ) with flask.current_app.db.session as session: session.add(code) session.commit() return code.code
def get_authorization_url(): """ Provide a redirect to the authorization endpoint from the OP. """ redirect = flask.request.args.get("redirect") if redirect and not redirect.startswith("/"): raise UserError("only support relative redirect") if redirect: flask.session["redirect"] = redirect requested_idp = flask.request.args.get("idp", "default") client = get_oauth_client(idp=requested_idp) # This will be the value that was put in the ``metadata`` in config. state_prefix = client.metadata.get("state_prefix") authorize_url = client.metadata.get("authorize_url") state = generate_token() if state_prefix: state = state_prefix + "-" + state # Get the authorization URL and the random state; save the state to check # later, and return the URL. (authorization_url, state) = client.create_authorization_url( authorize_url, state=state ) flask.session["state"] = state flask.session["idp"] = requested_idp return flask.redirect(authorization_url)
def storeRefreshToken(self, token, tokenID=None): """Store refresh token :param dict token: tokens as dict :param str tokenID: token ID :return: S_OK(dict)/S_ERROR() """ iat = int(time.time()) jti = tokenID or generate_token(10) self.log.debug("Store %s token:\n" % jti, pprint.pformat(token)) session = self.session() try: session.add( RefreshToken(jti=jti, issued_at=iat, access_token=token["access_token"], refresh_token=token.get("refresh_token"))) except Exception as e: return self.__result( session, S_ERROR("Could not add refresh token: %s" % repr(e))) self.log.info("Token with %s ID successfully added:\n" % jti, pprint.pformat(token)) return S_OK(dict(jti=jti, iat=iat))
async def request_sms(prefix: str, number: str, ctx=Depends(login_context)): if prefix not in config.SMS_SUPPORTED_PREFIX: raise HTTPException(status.HTTP_400_BAD_REQUEST, f"{prefix} not supported.") is_demo_account = number in config.SMS_DEMO_ACCOUNTS if is_demo_account: code = config.SMS_DEMO_CODE else: code = generate_token(config.SMS_CODE_LENGTH, config.SMS_CODE_CHARS) sms = await LoginSMS.create(prefix=prefix, number=number, code=code) if not is_demo_account and provider: try: await provider.send_login_code(f"{prefix}{number}", code, config.SMS_TTL) except SMSError as error: raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, error.code) else: log.critical( "Provider %s, is demo account %s! Send %s to %s%s", provider, is_demo_account, code, prefix, number, ) return dict(id=sms.id, ttl=config.SMS_TTL, cool_down=config.SMS_COOL_DOWN)
def test_code_challenge(self): sess = OAuth2Client(client_id=self.client_id, code_challenge_method='S256') url = 'https://example.com/authorize' auth_url, _ = sess.create_authorization_url( url, code_verifier=generate_token(48)) self.assertIn('code_challenge', auth_url) self.assertIn('code_challenge_method=S256', auth_url)
def create_authorization_verifier(self, request): func = self._hooks['create_authorization_verifier'] if callable(func): verifier = generate_token(36) func(request.credential, request.user, verifier) return verifier raise RuntimeError('"create_authorization_verifier" hook is required.')
def generate_authorization_code(self): """"The method to generate "code" value for authorization code data. Developers may rewrite this method, or customize the code length with:: class MyAuthorizationCodeGrant(AuthorizationCodeGrant): AUTHORIZATION_CODE_LENGTH = 32 # default is 48 """ return generate_token(self.AUTHORIZATION_CODE_LENGTH)
def test_code_challenge(): sess = OAuth2Client('foo', code_challenge_method='S256') url = 'https://example.com/authorize' auth_url, _ = sess.create_authorization_url( url, code_verifier=generate_token(48)) assert 'code_challenge=' in auth_url assert 'code_challenge_method=S256' in auth_url
def generate_key(cls, bit_size=256, options=None, is_private=True): if not is_private: raise ValueError('oct key can not be generated as public') if bit_size % 8 != 0: raise ValueError('Invalid bit size for oct key') return cls.import_key(generate_token(bit_size / 8), options)
async def upload_picture( user_id: str, file: UploadFile = File(..., media_type='application/octet-stream'), user: UserInfo = Depends(Authentication(auto_error=False)), registration_token: Optional[str] = Header(None, alias="x-token"), ): """Uploads a new picture for the passed user.""" if user is not None: is_admin = 'admin' in user['roles'] is_self = user.sub == user_id if not is_self or not is_admin: raise HTTPException(401) user_data = await async_user_collection.find_one({'_id': user_id}) elif registration_token is not None: check_token(registration_token) user_data = await async_user_collection.find_one( {'registration_token': registration_token}) else: raise HTTPException(401) if user_data is None: raise HTTPException(404) user = User.validate(user_data) if user.picture is None: user.picture = generate_token(48) updated_at = int(time.time()) await async_user_collection.update_one( {'_id': user_id}, {'$set': { 'picture': user.picture, 'updated_at': updated_at }}) await async_client_user_cache_collection.update_many( {'user_id': user_data['_id']}, {'$set': { 'last_modified': updated_at }}, ) else: try: await async_user_picture_bucket.delete(user.picture) except gridfs.errors.NoFile: pass hash_ = hashlib.sha512() while True: chunk = await file.read(4 * 1024) if not chunk: break hash_.update(chunk) await file.seek(0) file.file.seek(0) await async_user_picture_bucket.upload_from_stream_with_id( user.picture, user.id, file.file, metadata={ 'content_type': file.content_type, 'hash': hash_.digest() })
def test_merge_users(create_user, dummy_user, dummy_application, dummy_token, create_application, test_client): source_user = create_user(123) # app on both users (already exists on dummy user via dummy token) app_link = OAuthApplicationUserLink(application=dummy_application, user=source_user, scopes=['read:user', 'write:legacy_api']) token_string = generate_token() OAuthToken(access_token=token_string, app_user_link=app_link, scopes=['read:user']) # app only on source user test_app = create_application(name='test') app_link2 = OAuthApplicationUserLink(application=test_app, user=source_user, scopes=['read:user']) token_string2 = generate_token() OAuthToken(access_token=token_string2, app_user_link=app_link2, scopes=['read:user']) OAuthToken(access_token=generate_token(), app_user_link=app_link2, scopes=['read:user']) OAuthToken(access_token=generate_token(), app_user_link=app_link2, scopes=['read:user']) resp = test_client.get('/api/user/', headers={'Authorization': f'Bearer {dummy_token._plaintext_token}'}) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id for token in (token_string, token_string2): resp = test_client.get('/api/user/', headers={'Authorization': f'Bearer {token}'}) assert resp.status_code == 200 assert resp.json['id'] == source_user.id old_token_count = OAuthToken.query.count() merge_users(source_user, dummy_user) # source user should not have any leftover app links assert not source_user.oauth_app_links.count() # two app links on the target user assert dummy_user.oauth_app_links.count() == 2 # dummy app has one token from each user assert dummy_user.oauth_app_links.filter_by(application=dummy_application).one().tokens.count() == 2 # test app has 3 tokens coming from source user assert dummy_user.oauth_app_links.filter_by(application=test_app).one().tokens.count() == 3 # the total number of tokens didn't change (we do not delete surplus tokens during merge anyway) assert OAuthToken.query.count() == old_token_count # all tokens point to the target user for token in (dummy_token._plaintext_token, token_string, token_string2): resp = test_client.get('/api/user/', headers={'Authorization': f'Bearer {token}'}) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id
def test_invalid_code(client, oauth_client): """ Test that a client can't just send in a garbage code. """ code = generate_token(50) token_response = oauth2.post_token(client, oauth_client, code) assert token_response.status_code == 400 assert 'error' in token_response.json assert token_response.json['error'] == 'invalid_request'
def test_invalid_code(oauth_test_client): """ Test that a client can't just send in a garbage code. """ code = generate_token(50) response = oauth_test_client.token(code=code, do_asserts=False).response assert response.status_code == 400 assert "error" in response.json assert response.json["error"] == "invalid_request"
def generate_key(cls, key_size=256, options=None, is_private=False): """Generate a ``OctKey`` with the given bit size.""" if not is_private: raise ValueError('oct key can not be generated as public') if key_size % 8 != 0: raise ValueError('Invalid bit size for oct key') return cls.import_key(generate_token(key_size // 8), options)
def login(): redirect_uri = url_for('.auth', _external=True) conf_key = '{}_AUTHORIZE_PARAMS'.format(backend.OAUTH_NAME.upper()) params = current_app.config.get(conf_key, {}) if 'oidc' in backend.OAUTH_TYPE: nonce = generate_token(20) session[nonce_key] = nonce params['nonce'] = nonce return remote.authorize_redirect(redirect_uri, **params)
def login(): if current_app.config['SKIP_LOGIN']: payload = {'error': None, 'token': app_token_from_dummy()} return render_template('login_result.html', payload=payload) session['oidc.nonce'] = nonce = generate_token(20) return oauth.oidc.authorize_redirect(url_for('.login_oauth_oidc', _external=True), nonce=nonce)
async def request_sms(prefix: str, number: str): if prefix not in config.SMS_SUPPORTED_PREFIX: raise HTTPException(status.HTTP_400_BAD_REQUEST, f"{prefix} not supported.") code = generate_token(config.SMS_CODE_LENGTH, config.SMS_CODE_CHARS) sms = await LoginSMS.create(prefix=prefix, number=number, code=code) # TODO: send SMS here log.critical("No SMS provider! Send %s to %s%s", code, prefix, number) return dict(id=sms.id, ttl=config.SMS_TTL, cool_down=config.SMS_COOL_DOWN)
def login(request): redirect_uri = request.build_absolute_uri(reverse(auth_route_name)) params = {} if authorize_params: params.udpate(authorize_params) if 'oidc' in backend.OAUTH_TYPE: nonce = generate_token(20) request.session[nonce_key] = nonce params['nonce'] = nonce return remote.authorize_redirect(request, redirect_uri, **params)
def create_authorization_verifier(self, request): key_prefix = self._temporary_credential_key_prefix verifier = generate_token(36) credential = request.credential user = request.user key = key_prefix + credential.get_oauth_token() credential['oauth_verifier'] = verifier credential['user_id'] = user.pk cache.set(key, credential, timeout=self._temporary_expires_in) return verifier
def dummy_token(db, dummy_app_link): """Return a token for the dummy app/user.""" token_string = TOKEN_PREFIX_OAUTH + generate_token() token = OAuthToken(access_token=token_string, app_user_link=dummy_app_link, scopes=['read:legacy_api', 'read:user']) token._plaintext_token = token_string db.session.add(token) db.session.flush() return token
def create_authorization_code(self, client, grant_user, request): code = generate_token(48) expires = datetime.utcnow() + timedelta(seconds=GRANT_EXPIRATION) scopes = request.scope.split(' ') if request.scope else client.scopes OAuth2Grant.objects.create( code=code, client=client, redirect_uri=request.redirect_uri, scopes=scopes, user=ObjectId(grant_user.id), expires=expires, ) return code