Ejemplo n.º 1
0
def create_response_mode_response(redirect_uri, params, response_mode=None):
    if response_mode is None:
        response_mode = os.getenv('AUTHLIB_DEFAULT_RESPONSE_CODE', 'fragment')

    if response_mode == 'form_post':
        tpl = (
            '<html><head><title>Authlib</title></head>'
            '<body onload="javascript:document.forms[0].submit()">'
            '<form method="post" action="{}">{}</form></body></html>'
        )
        inputs = ''.join([
            '<input type="hidden" name="{}" value="{}"/>'.format(
                quote_url(k), quote_url(v))
            for k, v in params
        ])
        body = tpl.format(quote_url(redirect_uri), inputs)
        return 200, body, [('Content-Type', 'text/html; charset=utf-8')]

    if response_mode == 'query':
        uri = add_params_to_uri(redirect_uri, params, fragment=False)
    elif response_mode == 'fragment':
        uri = add_params_to_uri(redirect_uri, params, fragment=True)
    else:
        raise InvalidRequestError('Invalid "response_mode" value')

    return 302, '', [('Location', uri)]
Ejemplo n.º 2
0
    def test_invalid_request_parameters(self):
        self.prepare_data()
        handle = self.create_route()
        url = '/user'

        # case 1
        request = self.factory.get(url)
        resp = handle(request)
        data = json.loads(to_unicode(resp.content))
        self.assertEqual(data['error'], 'missing_required_parameter')
        self.assertIn('oauth_consumer_key', data['error_description'])

        # case 2
        request = self.factory.get(
            add_params_to_uri(url, {'oauth_consumer_key': 'a'}))
        resp = handle(request)
        data = json.loads(to_unicode(resp.content))
        self.assertEqual(data['error'], 'invalid_client')

        # case 3
        request = self.factory.get(
            add_params_to_uri(url, {'oauth_consumer_key': 'client'}))
        resp = handle(request)
        data = json.loads(to_unicode(resp.content))
        self.assertEqual(data['error'], 'missing_required_parameter')
        self.assertIn('oauth_token', data['error_description'])

        # case 4
        request = self.factory.get(
            add_params_to_uri(url, {
                'oauth_consumer_key': 'client',
                'oauth_token': 'a'
            })
        )
        resp = handle(request)
        data = json.loads(to_unicode(resp.content))
        self.assertEqual(data['error'], 'invalid_token')

        # case 5
        request = self.factory.get(
            add_params_to_uri(url, {
                'oauth_consumer_key': 'client',
                'oauth_token': 'valid-token'
            })
        )
        resp = handle(request)
        data = json.loads(to_unicode(resp.content))
        self.assertEqual(data['error'], 'missing_required_parameter')
        self.assertIn('oauth_timestamp', data['error_description'])
Ejemplo n.º 3
0
        def absolute_login_url(provider_id, fence_idp=None, shib_idp=None):
            """
            Args:
                provider_id (str): provider to log in with; an IDP_URL_MAP key.
                fence_idp (str, optional): if provider_id is "fence"
                    (multi-tenant Fence setup), fence_idp can be any of the
                    providers supported by the other Fence. If not specified,
                    will default to NIH login.
                shib_idp (str, optional): if provider_id is "fence" and
                    fence_idp is "shibboleth", shib_idp can be any Shibboleth/
                    InCommon provider. If not specified, will default to NIH
                    login.

            Returns:
                str: login URL for this provider, including extra query
                    parameters if fence_idp and/or shib_idp are specified.
            """
            try:
                base_url = config["BASE_URL"].rstrip("/")
                login_url = base_url + "/login/{}".format(
                    IDP_URL_MAP[provider_id])
            except KeyError as e:
                raise InternalError(
                    "identity provider misconfigured: {}".format(str(e)))

            params = {}
            if fence_idp:
                params["idp"] = fence_idp
            if shib_idp:
                params["shib_idp"] = shib_idp
            login_url = add_params_to_uri(login_url, params)

            return login_url
Ejemplo n.º 4
0
    def test_invalid_request(self):
        self.prepare_data()
        headers = self.create_basic_header(
            'password-client', 'password-secret'
        )

        rv = self.client.get(add_params_to_uri('/oauth/token', {
            'grant_type': 'password',
        }), headers=headers)
        resp = json.loads(rv.data)
        self.assertEqual(resp['error'], 'invalid_grant')

        rv = self.client.post('/oauth/token', data={
            'grant_type': 'password',
        }, headers=headers)
        resp = json.loads(rv.data)
        self.assertEqual(resp['error'], 'invalid_request')

        rv = self.client.post('/oauth/token', data={
            'grant_type': 'password',
            'username': '******',
        }, headers=headers)
        resp = json.loads(rv.data)
        self.assertEqual(resp['error'], 'invalid_request')

        rv = self.client.post('/oauth/token', data={
            'grant_type': 'password',
            'username': '******',
            'password': '******',
        }, headers=headers)
        resp = json.loads(rv.data)
        self.assertEqual(resp['error'], 'invalid_request')
Ejemplo n.º 5
0
    def get(self):
        """Handle ``GET /login/fence``."""
        oauth2_redirect_uri = flask.current_app.fence_client.client_kwargs.get(
            "redirect_uri")
        redirect_url = flask.request.args.get("redirect")
        if redirect_url:
            validate_redirect(redirect_url)
            flask.session["redirect"] = redirect_url
        (
            authorization_url,
            state,
        ) = flask.current_app.fence_client.generate_authorize_redirect(
            oauth2_redirect_uri, prompt="login")

        # add idp parameter to the authorization URL
        if "idp" in flask.request.args:
            idp = flask.request.args["idp"]
            flask.session["fence_idp"] = idp
            params = {"idp": idp}
            # if requesting to login through Shibboleth, also add shib_idp
            # parameter to the authorization URL
            if idp == "shibboleth" and "shib_idp" in flask.request.args:
                shib_idp = flask.request.args["shib_idp"]
                params["shib_idp"] = shib_idp
                flask.session["shib_idp"] = shib_idp
            authorization_url = add_params_to_uri(authorization_url, params)

        flask.session["state"] = state
        return flask.redirect(authorization_url)
Ejemplo n.º 6
0
def add_to_uri(token, uri):
    """Add a Bearer Token to the request URI.
    Not recommended, use only if client can't use authorization header or body.

    http://www.example.com/path?access_token=h480djs93hd8
    """
    return add_params_to_uri(uri, [('access_token', token)])
Ejemplo n.º 7
0
    def create_authorization_response(self, grant_user):
        state = self.request.state
        if grant_user:
            self.request.user = grant_user
            client = self.request.client
            token = self.generate_token(client,
                                        self.GRANT_TYPE,
                                        scope=self.request.scope,
                                        include_refresh_token=False)
            if self.request.response_type == 'id_token':
                token = {
                    'expires_in': token['expires_in'],
                    'scope': token['scope'],
                }
                token = self.process_token(token, self.request)
            else:
                log.debug('Grant token {!r} to {!r}'.format(token, client))
                self.server.save_token(token, self.request)
                token = self.process_token(token, self.request)
            params = [(k, token[k]) for k in token]
            if state:
                params.append(('state', state))
        else:
            error = AccessDeniedError(state=state)
            params = error.get_body()

        uri = add_params_to_uri(self.redirect_uri, params, fragment=True)
        headers = [('Location', uri)]
        return 302, '', headers
Ejemplo n.º 8
0
    def create_valid_authorization_response(self, request, grant_user):
        """Validate authorization request and create authorization response.

        :param request: OAuth2Request instance.
        :param grant_user: if granted, it is resource owner. If denied,
            it is None.
        :returns: (status_code, body, headers)
        """
        # TODO: rename it to `create_authorization_response` in v0.8
        try:
            grant = self.get_authorization_grant(request)
        except InvalidGrantError as error:
            return error(translations=self.get_translations(),
                         error_uris=self.get_error_uris())

        try:
            grant.validate_authorization_request()
            return grant.create_authorization_response(grant_user)
        except OAuth2Error as error:
            if grant.redirect_uri:
                params = error.get_body()
                loc = add_params_to_uri(grant.redirect_uri, params)
                headers = [('Location', loc)]
                return 302, '', headers
            return error(translations=self.get_translations(),
                         error_uris=self.get_error_uris())
Ejemplo n.º 9
0
 def __call__(self, translations=None, error_uris=None):
     if self.redirect_uri:
         params = self.get_body()
         loc = add_params_to_uri(
             self.redirect_uri, params, self.redirect_fragment)
         return 302, '', [('Location', loc)]
     return super(OAuth2Error, self).__call__(translations, error_uris)
Ejemplo n.º 10
0
    def create_authorization_response(self, request=None, grant_user=None):
        """Validate authorization request and create authorization response.

        :param request: OAuth2Request instance.
        :param grant_user: if granted, it is resource owner. If denied,
            it is None.
        :returns: Response
        """
        request = self.create_oauth2_request(request)
        try:
            grant = self.get_authorization_grant(request)
        except InvalidGrantError as error:
            return self.handle_error_response(request, error)

        try:
            grant.validate_authorization_request()
            args = grant.create_authorization_response(grant_user)
            return self.handle_response(*args)

        except OAuth2Error as error:
            if grant.redirect_uri:
                params = error.get_body()
                loc = add_params_to_uri(grant.redirect_uri, params)
                headers = [('Location', loc)]
                return self.handle_response(302, '', headers)
            return self.handle_error_response(request, error)
Ejemplo n.º 11
0
    def create_authorization_response(self, redirect_uri, grant_user):
        """If the resource owner grants the access request, the authorization
        server issues an authorization code and delivers it to the client by
        adding the following parameters to the query component of the
        redirection URI using the "application/x-www-form-urlencoded" format.
        Per `Section 4.1.2`_.

        code
            REQUIRED.  The authorization code generated by the
            authorization server. The authorization code MUST expire
            shortly after it is issued to mitigate the risk of leaks. A
            maximum authorization code lifetime of 10 minutes is
            RECOMMENDED. The client MUST NOT use the authorization code
            more than once. If an authorization code is used more than
            once, the authorization server MUST deny the request and SHOULD
            revoke (when possible) all tokens previously issued based on
            that authorization code.  The authorization code is bound to
            the client identifier and redirection URI.
        state
            REQUIRED if the "state" parameter was present in the client
            authorization request.  The exact value received from the
            client.

        For example, the authorization server redirects the user-agent by
        sending the following HTTP response.

        .. code-block:: http

            HTTP/1.1 302 Found
            Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
                   &state=xyz

        .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc6749#section-4.1.2

        :param redirect_uri: Redirect to the given URI for the authorization
        :param grant_user: if resource owner granted the request, pass this
            resource owner, otherwise pass None.
        :returns: (status_code, body, headers)
        """
        state = self.request.state
        if grant_user:
            self.request.user = grant_user

            if hasattr(self, 'create_authorization_code'):
                # TODO: deprecate
                code = self.create_authorization_code(self.request.client,
                                                      grant_user, self.request)
            else:
                code = self.generate_authorization_code()
                self.save_authorization_code(code, self.request)

            params = [('code', code)]
            if state:
                params.append(('state', state))
            uri = add_params_to_uri(redirect_uri, params)
            headers = [('Location', uri)]
            return 302, '', headers

        else:
            raise AccessDeniedError(state=state, redirect_uri=redirect_uri)
Ejemplo n.º 12
0
def encode_none(client, method, uri, headers, body):
    if method == 'GET':
        uri = add_params_to_uri(uri, [('client_id', client.client_id)])
        return uri, headers, body
    body = add_params_to_qs(body, [('client_id', client.client_id)])
    if 'Content-Length' in headers:
        headers['Content-Length'] = str(len(body))
    return uri, headers, body
Ejemplo n.º 13
0
 def auth_client(client, method, uri, headers, body):
     uri = add_params_to_uri(uri, [
         ('client_id', client.client_id),
         ('client_secret', client.client_secret),
     ])
     uri = uri + '&' + body
     body = ''
     return uri, headers, body
Ejemplo n.º 14
0
    def create_valid_authorization_response(self, request, grant_user=None):
        """Validate authorization request and create authorization response.
        Assume the endpoint for authorization request is
        ``https://photos.example.net/authorize``, the client redirects Jane's
        user-agent to the server's Resource Owner Authorization endpoint to
        obtain Jane's approval for accessing her private photos::

            https://photos.example.net/authorize?oauth_token=hh5s93j4hdidpola

        The server requests Jane to sign in using her username and password
        and if successful, asks her to approve granting 'printer.example.com'
        access to her private photos.  Jane approves the request and her
        user-agent is redirected to the callback URI provided by the client
        in the previous request (line breaks are for display purposes only)::

            http://printer.example.com/ready?
            oauth_token=hh5s93j4hdidpola&oauth_verifier=hfdp7dh39dks9884

        :param request: OAuth1Request instance.
        :param grant_user: if granted, pass the grant user, otherwise None.
        :returns: (status_code, body, headers)
        """
        # authorize endpoint should try catch this error
        self.validate_authorization_request(request)

        temporary_credentials = request.credential
        redirect_uri = temporary_credentials.get_redirect_uri()
        if not redirect_uri or redirect_uri == 'oob':
            client_id = temporary_credentials.get_client_id()
            client = self.get_client_by_id(client_id)
            redirect_uri = client.get_default_redirect_uri()

        if grant_user is None:
            error = AccessDeniedError()
            location = add_params_to_uri(redirect_uri, error.get_body())
            return 302, '', [('Location', location)]

        request.grant_user = grant_user
        verifier = self.create_authorization_verifier(request)

        params = [
            ('oauth_token', request.token),
            ('oauth_verifier', verifier)
        ]
        location = add_params_to_uri(redirect_uri, params)
        return 302, '', [('Location', location)]
Ejemplo n.º 15
0
 def _add_extra_info(url, headers, body):
     params = {'site': site}
     api_key = session._kwargs.get('api_key') if hasattr(session, '_kwargs') else os.environ['STACKOVERFLOW_KEY']
     api_filter = session._kwargs.get('api_filter') if hasattr(session, '_kwargs') else None
     if api_key:
         params['key'] = api_key
     if api_filter:
         params['filter'] = api_filter
     url = add_params_to_uri(url, params)
     return url, headers, body
Ejemplo n.º 16
0
    def test_invalid_request_parameters(self):
        self.prepare_data()
        url = '/user'

        # case 1
        rv = self.client.get(url)
        data = json.loads(rv.data)
        self.assertEqual(data['error'], 'missing_required_parameter')
        self.assertIn('oauth_consumer_key', data['error_description'])

        # case 2
        rv = self.client.get(
            add_params_to_uri(url, {'oauth_consumer_key': 'a'}))
        data = json.loads(rv.data)
        self.assertEqual(data['error'], 'invalid_client')

        # case 3
        rv = self.client.get(
            add_params_to_uri(url, {'oauth_consumer_key': 'client'}))
        data = json.loads(rv.data)
        self.assertEqual(data['error'], 'missing_required_parameter')
        self.assertIn('oauth_token', data['error_description'])

        # case 4
        rv = self.client.get(
            add_params_to_uri(url, {
                'oauth_consumer_key': 'client',
                'oauth_token': 'a'
            })
        )
        data = json.loads(rv.data)
        self.assertEqual(data['error'], 'invalid_token')

        # case 5
        rv = self.client.get(
            add_params_to_uri(url, {
                'oauth_consumer_key': 'client',
                'oauth_token': 'valid-token'
            })
        )
        data = json.loads(rv.data)
        self.assertEqual(data['error'], 'missing_required_parameter')
        self.assertIn('oauth_timestamp', data['error_description'])
Ejemplo n.º 17
0
 def _add_extra_info(url, headers, body):
     params = {'site': site}
     api_key = session._kwargs.get('api_key')
     api_filter = session._kwargs.get('api_filter')
     if api_key:
         params['key'] = api_key
     if api_filter:
         params['filter'] = api_filter
     url = add_params_to_uri(url, params)
     return url, headers, body
Ejemplo n.º 18
0
def prepare_grant_uri(uri,
                      client_id,
                      response_type,
                      redirect_uri=None,
                      scope=None,
                      state=None,
                      **kwargs):
    """Prepare the authorization grant request URI.

    The client constructs the request URI by adding the following
    parameters to the query component of the authorization endpoint URI
    using the ``application/x-www-form-urlencoded`` format:

    :param uri: The authorize endpoint to fetch "code" or "token".
    :param client_id: The client identifier as described in `Section 2.2`_.
    :param response_type: To indicate which OAuth 2 grant/flow is required,
                          "code" and "token".
    :param redirect_uri: The client provided URI to redirect back to after
                         authorization as described in `Section 3.1.2`_.
    :param scope: The scope of the access request as described by
                  `Section 3.3`_.
    :param state: An opaque value used by the client to maintain
                  state between the request and callback.  The authorization
                  server includes this value when redirecting the user-agent
                  back to the client.  The parameter SHOULD be used for
                  preventing cross-site request forgery as described in
                  `Section 10.12`_.
    :param kwargs: Extra arguments to embed in the grant/authorization URL.

    An example of an authorization code grant authorization URL::

        /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

    .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
    .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
    .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
    .. _`section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
    """
    params = [('response_type', response_type), ('client_id', client_id)]

    if redirect_uri:
        params.append(('redirect_uri', redirect_uri))
    if scope:
        params.append(('scope', list_to_scope(scope)))
    if state:
        params.append(('state', state))

    for k in kwargs:
        if kwargs[k]:
            params.append((to_unicode(k), kwargs[k]))

    return add_params_to_uri(uri, params)
Ejemplo n.º 19
0
 def test_consent_view(self):
     self.prepare_data()
     rv = self.client.get(add_params_to_uri('/oauth/authorize', {
         'response_type': 'id_token',
         'client_id': 'implicit-client',
         'scope': 'openid profile',
         'state': 'foo',
         'redirect_uri': 'https://a.b/c',
         'user_id': '1'
     }))
     self.assertIn(b'error=invalid_request', rv.data)
     self.assertIn(b'nonce', rv.data)
Ejemplo n.º 20
0
def _get_authorize_error_response(error, redirect_uri):
    """
    Get error response as defined by OIDC spec.

    Args:
        error (authlib.oauth2.rfc6749.error.OAuth2Error): Specific Oauth2 error
        redirect_uri (str): Redirection url
    """
    params = error.get_body()
    uri = add_params_to_uri(redirect_uri, params)
    headers = [("Location", uri)]
    response = flask.Response("", status=302, headers=headers)
    return response
 def test_consent_view(self):
     self.prepare_data()
     rv = self.client.get(
         add_params_to_uri(
             "/oauth/authorize",
             {
                 "response_type": "id_token",
                 "client_id": "implicit-client",
                 "scope": "openid profile",
                 "state": "foo",
                 "redirect_uri": "https://a.b/c",
                 "user_id": "1",
             },
         ))
     self.assertIn("error=invalid_request", rv.json())
     self.assertIn("nonce", rv.json())
Ejemplo n.º 22
0
    def create_authorization_response(self, grant_user):
        state = self.request.state
        if grant_user:
            self.request.user = grant_user
            client = self.request.client

            code = self.create_authorization_code(client, grant_user,
                                                  self.request)
            params = [('code', code)]

            token = self.generate_token(client,
                                        'implicit',
                                        scope=self.request.scope,
                                        include_refresh_token=False)

            response_types = self.request.response_type.split()
            if 'token' in response_types:
                log.debug('Grant token {!r} to {!r}'.format(token, client))
                self.server.save_token(token, self.request)
                if 'id_token' in response_types:
                    token = self.process_implicit_token(
                        token, self.request, code)
            else:
                # response_type is "code id_token"
                token = {
                    'expires_in': token['expires_in'],
                    'scope': token['scope']
                }
                token = self.process_implicit_token(token, self.request, code)

            params.extend([(k, token[k]) for k in token])
            if state:
                params.append(('state', state))
        else:
            error = AccessDeniedError(state=state)
            params = error.get_body()

        # http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
        fragment = True
        response_mode = self.request.response_mode
        if response_mode and response_mode == 'query':
            fragment = False

        uri = add_params_to_uri(self.redirect_uri, params, fragment=fragment)
        headers = [('Location', uri)]
        return 302, '', headers
Ejemplo n.º 23
0
    async def create_authorization_response(self, redirect_uri, grant_user):
        state = self.request.state
        if grant_user:
            self.request.user = grant_user

            code = self.generate_authorization_code()
            await self.save_authorization_code(code, self.request)

            params = [("code", code)]
            if state:
                params.append(("state", state))
            uri = add_params_to_uri(redirect_uri, params)
            headers = [('Location', uri)]
            return 302, '', headers

        else:
            raise AccessDeniedError(state=state, redirect_uri=redirect_uri)
Ejemplo n.º 24
0
    def create_authorization_url(self, url, request_token=None, **kwargs):
        """Create an authorization URL by appending request_token and optional
        kwargs to url.

        This is the second step in the OAuth 1 workflow. The user should be
        redirected to this authorization URL, grant access to you, and then
        be redirected back to you. The redirection back can either be specified
        during client registration or by supplying a callback URI per request.

        :param url: The authorization endpoint URL.
        :param request_token: The previously obtained request token.
        :param kwargs: Optional parameters to append to the URL.
        :returns: The authorization URL with new parameters embedded.
        """
        kwargs['oauth_token'] = request_token or self._client.token
        if self._client.redirect_uri:
            kwargs['oauth_callback'] = self._client.redirect_uri
        return add_params_to_uri(url, kwargs.items())
Ejemplo n.º 25
0
    def create_authorization_response(self, grant_user):
        state = self.request.state
        if grant_user:
            params = self._create_granted_params(grant_user)
            if state:
                params.append(('state', state))
        else:
            error = AccessDeniedError(state=state)
            params = error.get_body()

        # http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
        fragment = True
        response_mode = self.request.response_mode
        if response_mode and response_mode == 'query':
            fragment = False

        uri = add_params_to_uri(self.redirect_uri, params, fragment=fragment)
        headers = [('Location', uri)]
        return 302, '', headers
Ejemplo n.º 26
0
    def create_valid_authorization_response(self, uri, user):
        """Validate authorization request and create authorization response.

        :param uri: requested URI string
        :param user: if granted, user is resource owner. If denied, it is None.
        :returns: (status_code, body, headers)
        """
        try:
            grant = self.get_authorization_grant(uri)
        except InvalidGrantError as error:
            body = dict(error.get_body())
            return error.status_code, body, error.get_headers()
        try:
            grant.validate_authorization_request()
            return grant.create_authorization_response(user)
        except OAuth2Error as error:
            params = error.get_body()
            loc = add_params_to_uri(grant.redirect_uri, params)
            headers = [('Location', loc)]
            return 302, '', headers
Ejemplo n.º 27
0
    def create_authorization_response(
            self, request: Request,
            grant_user: typing.Optional[UserModelWrapper]) -> Response:
        error = "server_error"
        oauth2_request = self.create_oauth2_request(request)

        redirect_uri: typing.Optional[str]

        try:
            redirect_uri = self.get_redirect_uri_if_available(oauth2_request)
        except OAuth2Error as e:
            return PlainTextResponse(e.error, status_code=HTTP_400_BAD_REQUEST)

        if grant_user is None:
            error = "unauthorized_client"
        else:
            try:
                grant = self.get_authorization_grant(oauth2_request)
                redirect_uri = grant.validate_authorization_request()
                args = grant.create_authorization_response(
                    redirect_uri, grant_user)
                return self.handle_response(*args)
            except InvalidGrantError as e:
                logger.debug(f"{e}", exc_info=True)
                error = "unsupported_response_type"
            except OAuth2Error as e:
                error = e.error
            except Exception as e:
                logger.error(f"unexpected exception: {e}", exc_info=True)

        params = {
            "error": error,
        }
        if oauth2_request.state is not None:
            params["state"] = oauth2_request.state

        if redirect_uri is not None:
            return RedirectResponse(add_params_to_uri(redirect_uri, params))
        else:
            return PlainTextResponse(error, status_code=HTTP_400_BAD_REQUEST)
Ejemplo n.º 28
0
    def create_endpoint_response(self, request):
        # https://tools.ietf.org/html/rfc8628#section-3.1

        self.authenticate_client(request)
        self.server.validate_requested_scope(request.scope)

        device_code = self.generate_device_code()
        user_code = self.generate_user_code()
        verification_uri = self.get_verification_uri()
        verification_uri_complete = add_params_to_uri(
            verification_uri, [('user_code', user_code)])

        data = {
            'device_code': device_code,
            'user_code': user_code,
            'verification_uri': verification_uri,
            'verification_uri_complete': verification_uri_complete,
            'expires_in': self.EXPIRES_IN,
            'interval': self.INTERVAL,
        }

        self.save_device_credential(request.client_id, request.scope, data)
        return 200, data, default_json_headers
Ejemplo n.º 29
0
def load_settings(app):
    """
    load setttings from environment variables
    SECRET_KEY: app secret key to encrypt session cookies
    ENCRYPTION_KEY: encryption key to encrypt credentials in database
    POSTGRES_CREDS_FILE: JSON file with "db_username", "db_password",
        "db_host" and "db_database" keys
    SQLALCHEMY_DATABASE_URI: database connection uri. Overriden by
        POSTGRES_CREDS_FILE
    FENCE_BASE_URL: fence base url, eg: https://gen3_commons/user
    WTS_BASE_URL: base url for this workspace token service
    OIDC_CLIENT_ID: client id for the oidc client for this app
    OIDC_CLIENT_SECRET: client secret for the oidc client for this app
    AUTH_PLUGINS: a list of comma separate plugins, eg: k8s
    EXTERNAL_OIDC: config for additional oidc handshakes
    """
    app.secret_key = get_var("SECRET_KEY")
    app.encrytion_key = Fernet(get_var("ENCRYPTION_KEY"))
    postgres_creds = get_var("POSTGRES_CREDS_FILE", "")
    if postgres_creds:
        with open(postgres_creds, "r") as f:
            creds = json.load(f)
            app.config[
                "SQLALCHEMY_DATABASE_URI"
            ] = "postgresql://{db_username}:{db_password}@{db_host}:5432/{db_database}".format(
                **creds
            )
    else:
        app.config["SQLALCHEMY_DATABASE_URI"] = get_var("SQLALCHEMY_DATABASE_URI")
    url = get_var("FENCE_BASE_URL")
    fence_base_url = url if url.endswith("/") else (url + "/")

    wts_base_url = get_var("WTS_BASE_URL")
    if not wts_base_url.endswith("/"):
        wts_base_url = wts_base_url + "/"
    wts_hostname = urlparse(wts_base_url).netloc.split(".")[0]
    oauth_config = {
        "client_id": get_var("OIDC_CLIENT_ID"),
        "client_secret": get_var("OIDC_CLIENT_SECRET"),
        "api_base_url": fence_base_url,
        "authorize_url": fence_base_url + "oauth2/authorize",
        "access_token_url": fence_base_url + "oauth2/token",
        "redirect_uri": wts_base_url + "oauth2/authorize",
        "scope": "openid data user",
        "state_prefix": "",
    }
    app.config["OIDC"] = {"default": oauth_config}

    for conf in get_var("EXTERNAL_OIDC", []):
        url = get_var("BASE_URL", secret_config=conf)
        fence_base_url = (url if url.endswith("/") else (url + "/")) + "user/"
        # can redirect authorize callbacks to a shared central authorizer
        redirect_uri = get_var("REDIRECT_URI", default="", secret_config=conf)
        state_prefix = wts_hostname or ""
        if not redirect_uri:
            redirect_uri = wts_base_url + "oauth2/authorize"
            state_prefix = ""

        for idp, idp_conf in conf.get("login_options", {}).items():
            authorization_url = fence_base_url + "oauth2/authorize"
            authorization_url = add_params_to_uri(
                authorization_url, idp_conf.get("params", {})
            )
            app.config["OIDC"][idp] = {
                "client_id": get_var("OIDC_CLIENT_ID", secret_config=conf),
                "client_secret": get_var("OIDC_CLIENT_SECRET", secret_config=conf),
                "api_base_url": fence_base_url,
                "authorize_url": authorization_url,
                "access_token_url": fence_base_url + "oauth2/token",
                "redirect_uri": redirect_uri,
                "scope": "openid data user",
                "state_prefix": state_prefix,
            }

    app.config["SESSION_COOKIE_NAME"] = "wts"
    app.config["SESSION_COOKIE_SECURE"] = True
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
Ejemplo n.º 30
0
def authorize(*args, **kwargs):
    """
    OIDC Authorization Endpoint

    From the OIDC Specification:

    3.1.1.  Authorization Code Flow Steps
    The Authorization Code Flow goes through the following steps.

    - Client prepares an Authentication Request containing the desired request
      parameters.
    - Client sends the request to the Authorization Server.
    - Authorization Server Authenticates the End-User.
    - Authorization Server obtains End-User Consent/Authorization.
    - Authorization Server sends the End-User back to the Client with an
      Authorization Code.
    - Client requests a response using the Authorization Code at the Token
      Endpoint.
    - Client receives a response that contains an ID Token and Access Token in
      the response body.
    - Client validates the ID token and retrieves the End-User's Subject
      Identifier.

    Args:
        *args: additional arguments
        **kwargs: additional keyword arguments
    """
    need_authentication = False
    try:
        user = get_current_user()
    except Unauthorized:
        need_authentication = True

    if need_authentication or not user:
        redirect_url = config.get("BASE_URL") + flask.request.full_path
        params = {"redirect": redirect_url}
        login_url = config.get("DEFAULT_LOGIN_URL")
        idp = flask.request.args.get("idp")
        if idp:
            if idp not in IDP_URL_MAP or idp not in config["OPENID_CONNECT"]:
                raise UserError("idp {} is not supported".format(idp))
            idp_url = IDP_URL_MAP[idp]
            login_url = "{}/login/{}".format(config.get("BASE_URL"), idp_url)
        login_url = add_params_to_uri(login_url, params)
        return flask.redirect(login_url)

    try:
        grant = server.validate_consent_request(end_user=user)
    except OAuth2Error as e:
        raise Unauthorized("{} failed to authorize".format(str(e)))

    client_id = grant.client.client_id
    with flask.current_app.db.session as session:
        client = session.query(Client).filter_by(client_id=client_id).first()

    # TODO: any way to get from grant?
    confirm = flask.request.form.get("confirm") or flask.request.args.get(
        "confirm")
    if client.auto_approve:
        confirm = "yes"
    if confirm is not None:
        response = _handle_consent_confirmation(user, confirm)
        # if it's a 302 for POST confirm, return 200 instead and include
        # redirect url in body because browser ajax POST doesn't follow
        # cross origin redirect
        if flask.request.method == "POST" and response.status_code == 302:
            return flask.jsonify({"redirect": response.headers["Location"]})
    else:
        # no confirm param, so no confirmation has occured yet
        response = _authorize(user, grant, client)

    return response