Beispiel #1
0
def create_digital_user_code():
    base = '0123456789'
    return '-'.join([
        generate_token(3, base),
        generate_token(3, base),
        generate_token(3, base),
    ])
Beispiel #2
0
    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)
Beispiel #3
0
    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
Beispiel #4
0
    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)
Beispiel #5
0
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())
Beispiel #6
0
    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
Beispiel #7
0
    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
Beispiel #8
0
    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
Beispiel #9
0
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
Beispiel #11
0
    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)
Beispiel #13
0
    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))
Beispiel #14
0
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)
Beispiel #15
0
    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)
Beispiel #16
0
    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.')
Beispiel #17
0
    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)
Beispiel #18
0
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
Beispiel #19
0
    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)
Beispiel #20
0
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()
        })
Beispiel #21
0
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"
Beispiel #24
0
    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)
Beispiel #25
0
 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)
Beispiel #26
0
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)
Beispiel #27
0
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)
Beispiel #28
0
 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
Beispiel #30
0
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
Beispiel #31
0
 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