Example #1
0
    def test_parse_open_id_request(self):
        userinfo_claims = Claims(name={"essential": True},
                                 nickname=None,
                                 email={"essential": True},
                                 email_verified={"essential": True},
                                 picture=None)
        id_token_claims = Claims(auth_time={
            "essential": True,
            "acr": {
                "values": ["urn:mace:incommon:iap:silver"]
            }
        })
        claims_req = ClaimsRequest(userinfo=userinfo_claims,
                                   id_token=id_token_claims)

        oidreq = OpenIDRequest(response_type=["code", "id_token"],
                               client_id=CLIENT_ID,
                               redirect_uri="https://client.example.com/cb",
                               scope="openid profile",
                               state="n-0S6_WzA2Mj",
                               nonce="af0ifjsldkj",
                               max_age=86400,
                               claims=claims_req)

        request = self.srv.parse_open_id_request(data=oidreq.to_json(),
                                                 sformat="json")
        assert isinstance(request, OpenIDRequest)
        assert _eq(request.keys(), [
            'nonce', 'claims', 'state', 'redirect_uri', 'response_type',
            'client_id', 'scope', 'max_age'
        ])
        assert request["nonce"] == "af0ifjsldkj"
        assert "email" in request["claims"]["userinfo"]
Example #2
0
def test_claims_ser_json():
    claims = Claims(name={"essential": True}, nickname=None,
                    email={"essential": True},
                    email_verified={"essential": True}, picture=None)
    claims = claims_deser(claims_ser(claims, "json"), sformat="json")
    assert _eq(claims.keys(), ['name', 'nickname', 'email', 'email_verified',
                               'picture'])
Example #3
0
    def test_get_approved_attributes(self, frontend):
        claims_req = ClaimsRequest(id_token=Claims(email=None), userinfo=Claims(userinfo_claim=None))
        req = AuthorizationRequest(scope="openid profile", claims=claims_req)
        provider_supported_claims = ["email", "name", "given_name", "family_name", "userinfo_claim", "extra_claim"]

        result = frontend._get_approved_attributes(provider_supported_claims, req)
        assert Counter(result) == Counter(["email", "name", "given_name", "family_name", "userinfo_claim"])
Example #4
0
def test_claims_ser_json():
    claims = Claims(name={"essential": True}, nickname=None,
                    email={"essential": True},
                    email_verified={"essential": True}, picture=None)
    claims = claims_deser(claims_ser(claims, "json"), sformat="json")
    assert _eq(claims.keys(), ['name', 'nickname', 'email', 'email_verified',
                               'picture'])
Example #5
0
    def test_openid_request_with_request_1(self):
        claims = {
            "name": {
                "essential": True
            },
            "nickname": None,
            "email": {
                "essential": True
            },
            "verified": {
                "essential": True
            },
            "picture": None
        }

        areq = self.client.construct_AuthorizationRequest(
            request_args={
                "scope":
                "openid",
                "response_type": ["code"],
                "claims":
                ClaimsRequest(userinfo=Claims(**claims),
                              id_token=Claims(auth_time=None,
                                              acr={"values": ["2"]})),
                "max_age":
                86400,
            },
            request_param="request")

        print areq.to_dict()
        assert areq
        assert "request" in areq
Example #6
0
    def test_with_multiple_requested_sub(self):
        self.authn_request_args['claims'] = ClaimsRequest(
            userinfo=Claims(sub={'value': 'nomatch1'}),
            id_token=Claims(sub={'value': 'nomatch2'}))
        auth_req = AuthorizationRequest().from_dict(self.authn_request_args)
        with pytest.raises(AuthorizationError) as exc:
            self.provider.authorize(auth_req, TEST_USER_ID)

        assert 'different' in str(exc.value)
Example #7
0
def test_claims_ser_json():
    claims = Claims(
        name={"essential": True},
        nickname=None,
        email={"essential": True},
        email_verified={"essential": True},
        picture=None,
    )
    claims = claims_deser(claims_ser(claims, "json"), sformat="json")
    assert _eq(claims.keys(),
               ["name", "nickname", "email", "email_verified", "picture"])
Example #8
0
def test_claims_deser():
    pre = Claims(name={"essential": True}, nickname=None,
                   email={"essential": True},
                   email_verified={"essential": True}, picture=None)

    claims = claims_deser(pre.to_json(), format="json")
    assert _eq(claims.keys(), ['name', 'nickname', 'email', 'email_verified',
                               'picture'])

    claims = claims_deser(pre.to_dict(), format="dict")
    assert _eq(claims.keys(), ['name', 'nickname', 'email', 'email_verified',
                           'picture'])
Example #9
0
    def _collect_user_info(self, session, userinfo_claims=None):
        """
        Collect information about a user.
        This can happen in two cases, either when constructing an IdToken or
        when returning user info through the UserInfo endpoint

        :param session: Session information
        :param userinfo_claims: user info claims
        :return: User info
        """
        if userinfo_claims is None:
            uic = {}
            for scope in session["scope"]:
                try:
                    claims = dict([(name, None) for name in
                                   SCOPE2CLAIMS[scope]])
                    uic.update(claims)
                except KeyError:
                    pass

            if "oidreq" in session:
                oidreq = OpenIDRequest().deserialize(session["oidreq"], "json")
                logger.debug("OIDREQ: %s" % oidreq.to_dict())
                try:
                    _claims = oidreq["claims"]["userinfo"]
                except KeyError:
                    pass
                else:
                    for key, val in uic.items():
                        if key not in _claims:
                            _claims[key] = val
                    uic = _claims

                if uic:
                    userinfo_claims = Claims(**uic)
                else:
                    userinfo_claims = None
            elif uic:
                userinfo_claims = Claims(**uic)
            else:
                userinfo_claims = None

            logger.debug("userinfo_claim: %s" % userinfo_claims.to_dict())

        logger.debug("Session info: %s" % session)
        info = self.userinfo(session["local_sub"], userinfo_claims)

        info["sub"] = session["sub"]
        logger.debug("user_info_response: %s" % (info,))

        return info
Example #10
0
def authorize(user):
    if user.orcid is None:
        proofing_state = current_app.proofing_statedb.get_state_by_eppn(user.eppn, raise_on_missing=False)
        if not proofing_state:
            current_app.logger.debug(
                'No proofing state found for user {!s}. Initializing new proofing state.'.format(user)
            )
            proofing_state = OrcidProofingState(
                id=None, modified_ts=None, eppn=user.eppn, state=get_unique_hash(), nonce=get_unique_hash()
            )
            current_app.proofing_statedb.save(proofing_state)

        claims_request = ClaimsRequest(userinfo=Claims(id=None))
        oidc_args = {
            'client_id': current_app.oidc_client.client_id,
            'response_type': 'code',
            'scope': 'openid',
            'claims': claims_request.to_json(),
            'redirect_uri': url_for('orcid.authorization_response', _external=True),
            'state': proofing_state.state,
            'nonce': proofing_state.nonce,
        }
        authorization_url = '{}?{}'.format(current_app.oidc_client.authorization_endpoint, urlencode(oidc_args))
        current_app.logger.debug('Authorization url: {!s}'.format(authorization_url))
        current_app.stats.count(name='authn_request')
        return redirect(authorization_url)
    # Orcid already connected to user
    redirect_url = current_app.config.orcid_verify_redirect_url
    return redirect_with_msg(redirect_url, OrcidMsg.already_connected)
Example #11
0
def freja_proofing(user, nin):
    proofing_state = current_app.proofing_statedb.get_state_by_eppn(
        user.eppn, raise_on_missing=False)
    if not proofing_state:
        current_app.logger.debug(
            'No proofing state found for user {!s}. Initializing new proofing flow.'
            .format(user))
        proofing_state = helpers.create_proofing_state(user, nin)

        # Initiate authn request
        try:
            redirect_url = url_for('oidc_proofing.authorization_response',
                                   _external=True)
            claims_request = ClaimsRequest(userinfo=Claims(results=None))
            success = helpers.do_authn_request(proofing_state, claims_request,
                                               redirect_url)
            if not success:
                current_app.stats.count(name='freja.authn_request_op_error')
                return error_response(message=CommonMsg.temp_problem)
        except requests.exceptions.ConnectionError as e:
            current_app.logger.error(
                'No connection to authorization endpoint: {!s}'.format(e))
            return error_response(message=OIDCMsg.no_conn)

        # If authentication request went well save user state
        current_app.stats.count(name='freja.authn_request_success')
        current_app.proofing_statedb.save(proofing_state)
        current_app.logger.debug(
            'Proofing state {!s} for user {!s} saved'.format(
                proofing_state.state, user))
    # Add the nin used to initiate the proofing state to the user
    # NOOP if the user already have the nin
    add_nin_to_user(user, proofing_state)

    return get_freja_state()
Example #12
0
 def test_include_userinfo_claims_request_with_response_type_id_token(self):
     self.authn_request_args['claims'] = ClaimsRequest(userinfo=Claims(
         nickname=None)).to_json()
     self.provider.clients[TEST_CLIENT_ID]['response_types'] = ['id_token']
     self.authn_request_args['response_type'] = 'id_token'
     with pytest.raises(InvalidAuthenticationRequest):
         self.provider.parse_authentication_request(
             urlencode(self.authn_request_args))
Example #13
0
 def authn_req(self):
     state = "my_state"
     nonce = "nonce"
     redirect_uri = "https://client.example.com"
     claims_req = ClaimsRequest(id_token=Claims(email=None))
     req = AuthorizationRequest(client_id=CLIENT_ID, state=state, scope="openid",
                                response_type="id_token", redirect_uri=redirect_uri,
                                nonce=nonce, claims=claims_req)
     return req
Example #14
0
    def test_full_flow(self, satosa_config_dict, oidc_frontend_config, saml_backend_config, idp_conf):
        subject_id = "testuser1"

        # proxy config
        satosa_config_dict["FRONTEND_MODULES"] = [oidc_frontend_config]
        satosa_config_dict["BACKEND_MODULES"] = [saml_backend_config]
        satosa_config_dict["INTERNAL_ATTRIBUTES"]["attributes"] = {attr_name: {"openid": [attr_name],
                                                                               "saml": [attr_name]}
                                                                   for attr_name in USERS[subject_id]}
        _, backend_metadata = create_entity_descriptors(SATOSAConfig(satosa_config_dict))

        # application
        test_client = Client(make_app(SATOSAConfig(satosa_config_dict)), Response)

        # get frontend OP config info
        provider_config = json.loads(test_client.get("/.well-known/openid-configuration").data.decode("utf-8"))

        # create auth req
        claims_request = ClaimsRequest(id_token=Claims(**{k: None for k in USERS[subject_id]}))
        req_args = {"scope": "openid", "response_type": "id_token", "client_id": CLIENT_ID,
                    "redirect_uri": REDIRECT_URI, "nonce": "nonce",
                    "claims": claims_request.to_json()}
        auth_req = urlparse(provider_config["authorization_endpoint"]).path + "?" + urlencode(req_args)

        # make auth req to proxy
        proxied_auth_req = test_client.get(auth_req)
        assert proxied_auth_req.status == "303 See Other"

        # config test IdP
        backend_metadata_str = str(backend_metadata[saml_backend_config["name"]][0])
        idp_conf["metadata"]["inline"].append(backend_metadata_str)
        fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf))

        # create auth resp
        req_params = dict(parse_qsl(urlparse(proxied_auth_req.data.decode("utf-8")).query))
        url, authn_resp = fakeidp.handle_auth_req(
            req_params["SAMLRequest"],
            req_params["RelayState"],
            BINDING_HTTP_REDIRECT,
            subject_id,
            response_binding=BINDING_HTTP_REDIRECT)

        # make auth resp to proxy
        authn_resp_req = urlparse(url).path + "?" + urlencode(authn_resp)
        authn_resp = test_client.get(authn_resp_req)
        assert authn_resp.status == "303 See Other"

        # verify auth resp from proxy
        resp_dict = dict(parse_qsl(urlparse(authn_resp.data.decode("utf-8")).fragment))
        signing_key = RSAKey(key=rsa_load(oidc_frontend_config["config"]["signing_key_path"]),
                             use="sig", alg="RS256")
        id_token_claims = JWS().verify_compact(resp_dict["id_token"], keys=[signing_key])

        assert all(
            (name, values) in id_token_claims.items()
            for name, values in OIDC_USERS[subject_id].items()
        )
Example #15
0
def make_openid_request(arq,
                        keys=None,
                        userinfo_claims=None,
                        idtoken_claims=None,
                        algorithm=None,
                        **kwargs):
    """
    Construct the specification of what I want returned.
    The request will be signed

    :param arq: The Authorization request
    :param keys: Keys to use for signing/encrypting
    :param userinfo_claims: UserInfo claims
    :param idtoken_claims: IdToken claims
    :param algorithm: Which signing/encrypting algorithm to use
    :return: JWT encoded OpenID request
    """

    oir_args = {}
    for prop in OpenIDRequest.c_param.keys():
        try:
            oir_args[prop] = arq[prop]
        except KeyError:
            pass

    for attr in ["scope", "response_type"]:
        if attr in oir_args:
            oir_args[attr] = " ".join(oir_args[attr])

    c_args = {}
    if userinfo_claims is not None:
        # UserInfoClaims
        c_args["userinfo"] = Claims(**userinfo_claims)

    if idtoken_claims is not None:
        #IdTokenClaims
        c_args["id_token"] = Claims(**idtoken_claims)

    if c_args:
        oir_args["claims"] = ClaimsRequest(**c_args)

    oir = OpenIDRequest(**oir_args)

    return oir.to_jwt(key=keys, algorithm=algorithm)
Example #16
0
    def test_claims_request_not_allowed_for_client(self):
        client_id = 'client1'
        provider = Mock()
        provider.clients = {client_id: {'allowed_claims': []}}

        auth_req = AuthorizationRequest(
            claims=ClaimsRequest(id_token=Claims(domain=None)),
            client_id=client_id)
        with pytest.raises(InvalidAuthenticationRequest):
            claims_request_is_valid_for_client(provider, auth_req)
Example #17
0
    def test_valid_claims_request_for_client(self):
        client_id = 'client1'
        provider = Mock()
        provider.clients = {client_id: {'allowed_claims': ['domain']}}

        auth_req = AuthorizationRequest(
            claims=ClaimsRequest(id_token=Claims(domain=None)),
            client_id=client_id)
        # should not raise an exception
        claims_request_is_valid_for_client(provider, auth_req)
Example #18
0
 def test_authorize_includes_requested_id_token_claims_even_if_token_request_can_be_made(
         self):
     self.authn_request_args['response_type'] = ['id_token', 'token']
     self.authn_request_args['claims'] = ClaimsRequest(id_token=Claims(
         email=None))
     auth_req = AuthorizationRequest().from_dict(self.authn_request_args)
     resp = self.provider.authorize(auth_req, TEST_USER_ID)
     id_token = assert_id_token_base_claims(resp['id_token'],
                                            self.provider.signing_key,
                                            self.provider, auth_req)
     assert id_token['email'] == self.provider.userinfo[TEST_USER_ID][
         'email']
Example #19
0
def build_userinfo_claims(claims, sformat="signed", locale="us-en"):
    """
    config example::

        "userinfo":{
            "name": {"essential": true},
            "nickname": null,
            "email": {"essential": true},
            "email_verified": {"essential": true},
            "picture": null
        }
    """
    return Claims(format=sformat, **claims)
Example #20
0
    def test_handle_userinfo_with_custom_sub(self, monkeypatch):
        sub = 'test_sub1'
        monkeypatch.setitem(self.provider.userinfo._db[TEST_USER_ID], 'sub',
                            sub)
        claims_request = ClaimsRequest(userinfo=Claims(email=None))
        access_token = self.create_access_token({
            'scope': 'openid profile',
            'claims': claims_request
        })
        response = self.provider.handle_userinfo_request(
            urlencode({'access_token': access_token}))

        assert response['sub'] == sub
Example #21
0
    def test_handle_userinfo(self):
        claims_request = ClaimsRequest(userinfo=Claims(email=None))
        access_token = self.create_access_token({
            'scope': 'openid profile',
            'claims': claims_request
        })
        response = self.provider.handle_userinfo_request(
            urlencode({'access_token': access_token}))

        response_sub = response['sub']
        del response['sub']
        assert response.to_dict() == self.provider.userinfo[TEST_USER_ID]
        assert self.provider.authz_state.get_user_id_for_subject_identifier(
            response_sub) == TEST_USER_ID
Example #22
0
 def test_code_exchange_request_with_claims_requested_in_id_token(self):
     claims_req = {'claims': ClaimsRequest(id_token=Claims(email=None))}
     self.authorization_code_exchange_request_args[
         'code'] = self.create_authz_code(extra_auth_req_params=claims_req)
     response = self.provider._do_code_exchange(
         self.authorization_code_exchange_request_args, None)
     assert response[
         'access_token'] in self.provider.authz_state.access_tokens
     id_token = assert_id_token_base_claims(response['id_token'],
                                            self.provider.signing_key,
                                            self.provider,
                                            self.authn_request_args)
     assert id_token['email'] == self.provider.userinfo[TEST_USER_ID][
         'email']
Example #23
0
    def test_authorize_include_user_claims_from_scope_in_id_token_if_no_userinfo_req_can_be_made(
            self):
        self.authn_request_args['response_type'] = 'id_token'
        self.authn_request_args['scope'] = 'openid profile'
        self.authn_request_args['claims'] = ClaimsRequest(id_token=Claims(
            email={'essential': True}))
        auth_req = AuthorizationRequest().from_dict(self.authn_request_args)
        resp = self.provider.authorize(auth_req, TEST_USER_ID)

        id_token = IdToken().from_jwt(resp['id_token'],
                                      key=[self.provider.signing_key])
        # verify all claims are part of the ID Token
        assert all(
            id_token[claim] == value
            for claim, value in self.provider.userinfo[TEST_USER_ID].items())
Example #24
0
def get_state(**kwargs):
    eppn = mock_auth.authenticate(kwargs)
    current_app.logger.debug(
        'Getting state for user with eppn {}.'.format(eppn))
    proofing_state = current_app.proofing_statedb.get_state_by_eppn(
        eppn, raise_on_missing=False)
    if not proofing_state:
        current_app.logger.debug(
            'No proofing state found, initializing new proofing flow.'.format(
                eppn))
        state = get_unique_hash()
        nonce = get_unique_hash()
        token = get_unique_hash()
        proofing_state = OidcProofingState({
            'eduPersonPrincipalName': eppn,
            'state': state,
            'nonce': nonce,
            'token': token
        })
        claims_request = ClaimsRequest(
            userinfo=Claims(identity=None, vetting_time=None, metadata=None))
        # Initiate proofing
        response = do_authentication_request(state, nonce, token,
                                             claims_request)
        if response.status_code != 200:
            payload = {'error': response.reason, 'message': response.content}
            raise ApiException(status_code=response.status_code,
                               payload=payload)
        # If authentication request went well save user state
        current_app.proofing_statedb.save(proofing_state)
        current_app.logger.debug('Proofing state {} for user {} saved'.format(
            proofing_state.state, eppn))
    # Return nonce and nonce as qr code
    current_app.logger.debug('Returning nonce for user {}'.format(eppn))
    buf = BytesIO()
    qr_code = '1' + json.dumps({
        'nonce': proofing_state.nonce,
        'token': proofing_state.token
    })
    qrcode.make(qr_code).save(buf)
    qr_b64 = base64.b64encode(buf.getvalue()).decode()
    ret = {
        'qr_code': qr_code,
        'qr_img': '<img src="data:image/png;base64, {}"/>'.format(qr_b64),
    }
    return ret
Example #25
0
def seleg_proofing(user, nin):
    proofing_state = current_app.proofing_statedb.get_state_by_eppn(
        user.eppn, raise_on_missing=False)
    if not proofing_state:
        current_app.logger.debug(
            'No proofing state found for user {!s}. Initializing new proofing flow.'
            .format(user))
        proofing_state = helpers.create_proofing_state(user, nin)

        # Initiate authn request
        try:
            redirect_url = url_for('oidc_proofing.authorization_response',
                                   _external=True)
            claims_request = ClaimsRequest(userinfo=Claims(
                identity=None, vetting_time=None, metadata=None))
            success = helpers.do_authn_request(proofing_state, claims_request,
                                               redirect_url)
            if not success:
                current_app.stats.count(name='seleg.authn_request_op_error')
                return {
                    '_status': 'error',
                    'message': 'Temporary technical problems'
                }
        except requests.exceptions.ConnectionError as e:
            current_app.logger.error(
                'No connection to authorization endpoint: {!s}'.format(e))
            return {
                '_status': 'error',
                'message': 'No connection to authorization endpoint'
            }

        # If authentication request went well save user state
        current_app.stats.count(name='seleg.authn_request_success')
        current_app.proofing_statedb.save(proofing_state)
        current_app.logger.debug(
            'Proofing state {!s} for user {!s} saved'.format(
                proofing_state.state, user))
    # Add the nin used to initiate the proofing state to the user
    # NOOP if the user already have the nin
    add_nin_to_user(user, proofing_state)

    return get_seleg_state()
Example #26
0
def login_shi():
    session["state"] = rndstr()
    session["nonce"] = rndstr()
    claims_request = ClaimsRequest(
        userinfo=Claims(
            uiucedu_uin={"essential": True}
        )
    )
    args = {
        "client_id": client.client_id,
        "response_type": "code",
        "scope": Config.SCOPES,
        "claims": claims_request,
        "nonce": session["nonce"],
        "redirect_uri": client.registration_response["redirect_uris"][0],
        "state": session["state"]
    }
    auth_req = client.construct_AuthorizationRequest(request_args=args)
    login_url = auth_req.request(client.authorization_endpoint)
    return Redirect(login_url)
Example #27
0
    def claims_endpoint(self, request, http_authz, *args):
        _log_info = logger.info

        ucreq = self.srvmethod.parse_user_claims_request(request)

        _log_info("request: %s" % ucreq)

        try:
            resp = self.client_authn(self, ucreq, http_authz)
        except Exception as err:
            _log_info("Failed to verify client due to: %s" % err)
            resp = False

        if "claims_names" in ucreq:
            args = dict([(n, {
                "optional": True
            }) for n in ucreq["claims_names"]])
            uic = Claims(**args)
        else:
            uic = None

        _log_info("User info claims: %s" % uic)

        #oicsrv, userdb, subject, client_id="", user_info_claims=None
        info = self.userinfo(ucreq["sub"],
                             user_info_claims=uic,
                             client_id=ucreq["client_id"])

        _log_info("User info: %s" % info)

        # Convert to message format
        info = OpenIDSchema(**info)

        if self.do_aggregation(info, ucreq["sub"]):
            cresp = self._aggregation(info)
        else:
            cresp = self._distributed(info)

        _log_info("response: %s" % cresp.to_dict())

        return Response(cresp.to_json(), content="application/json")
Example #28
0
def login():
    session['state'] = rndstr()
    session['nonce'] = rndstr()

    # setup claim request
    claims_request = ClaimsRequest(
         userinfo = Claims(uiucedu_uin={"essential": True})
    )
    args = {
         "client_id": client.client_id,
         "response_type": "code",
         "scope": app.config["SCOPES"],
         "nonce": session["nonce"],
         "redirect_uri": app.config["REDIRECT_URIS"][0],
         "state": session["state"],
         "claims": claims_request
    }

    auth_req = client.construct_AuthorizationRequest(request_args=args)
    login_url = auth_req.request(client.authorization_endpoint)

    return Redirect(login_url)
Example #29
0
def get_authorization_url(client, state):
    args = {
        'client_id':
        client.client_id,
        'response_type':
        'code',
        'scope':
        'openid',
        'state':
        state,
        'redirect_uri':
        client.registration_response['redirect_uris'][0],
        'claims':
        ClaimsRequest(userinfo=Claims(
            given_name={'essential': True},
            family_name={'essential': True},
            email={'essential': True},
        )),
    }

    auth_req = client.construct_AuthorizationRequest(request_args=args)
    return auth_req.request(client.provider_info['authorization_endpoint'])
Example #30
0
def get_authorization_url(client, state):
    args = {
        "client_id":
        client.client_id,
        "response_type":
        "code",
        "scope":
        "openid",
        "state":
        state,
        "redirect_uri":
        client.registration_response["redirect_uris"][0],
        "claims":
        ClaimsRequest(userinfo=Claims(
            given_name={"essential": True},
            family_name={"essential": True},
            email={"essential": True},
        )),
    }

    auth_req = client.construct_AuthorizationRequest(request_args=args)
    return auth_req.request(client.provider_info["authorization_endpoint"])
Example #31
0
    def begin(self,
              scope="",
              response_type="",
              use_nonce=False,
              path="",
              **kwargs):
        """ Begin the OIDC flow

        :param scope: Defines which user info claims is wanted
        :param response_type: Controls the parameters returned in the
            response from the Authorization Endpoint
        :param use_nonce: If not implicit flow nonce is optional.
            This defines if it should be used anyway.
        :param path: The path part of the redirect URL
        :return: A 2-tuple, session identifier and URL to which the user
            should be redirected
        """
        _log_info = logger.info

        if self.debug:
            _log_info("- begin -")

        _page = self.config["authz_page"]
        if not path.endswith("/"):
            if _page.startswith("/"):
                self.redirect_uris = [path + _page]
            else:
                self.redirect_uris = ["%s/%s" % (path, _page)]
        else:
            if _page.startswith("/"):
                self.redirect_uris = [path + _page[1:]]
            else:
                self.redirect_uris = ["%s/%s" % (path, _page)]

        # Put myself in the dictionary of sessions, keyed on session-id
        if not self.seed:
            self.seed = rndstr()

        if not scope:
            scope = self.config["scope"]
        if not response_type:
            response_type = self.config["response_type"]

        sid = stateID(path, self.seed)
        self.grant[sid] = Grant(seed=self.seed)

        self._backup(sid)
        self.sdb["seed:%s" % self.seed] = sid

        args = {
            "client_id": self.client_id,
            "state": sid,
            "response_type": response_type,
            "scope": scope,
        }

        # nonce is REQUIRED in implicit flow,
        # OPTIONAL on code flow.
        if "token" in response_type or use_nonce:
            self.nonce = rndstr(12)
            args["nonce"] = self.nonce

        if "max_age" in self.config:
            args["max_age"] = self.config["max_age"]

        _claims = None
        if "user_info" in self.config:
            _claims = ClaimsRequest(userinfo=Claims(
                **self.config["user_info"]))
        if "id_token" in self.config:
            if _claims:
                _claims["id_token"] = Claims(**self.config["id_token"])
            else:
                _claims = ClaimsRequest(id_token=Claims(
                    **self.config["id_token"]))

        if _claims:
            args["claims"] = _claims

        if "request_method" in self.config:
            areq = self.construct_AuthorizationRequest(request_args=args,
                                                       extra_args=None,
                                                       request_param="request")

            if self.config["request_method"] == "file":
                id_request = areq["request"]
                del areq["request"]
                _filedir = self.config["temp_dir"]
                _webpath = self.config["temp_path"]
                _name = rndstr(10)
                filename = os.path.join(_filedir, _name)
                while os.path.exists(filename):
                    _name = rndstr(10)
                    filename = os.path.join(_filedir, _name)
                fid = open(filename, mode="w")
                fid.write(id_request)
                fid.close()
                _webname = "%s%s%s" % (path, _webpath, _name)
                areq["request_uri"] = _webname
                self.request_uri = _webname
                self._backup(sid)
        else:
            if "userinfo_claims" in args:  # can only be carried in an IDRequest
                raise PyoidcError("Need a request method")

            areq = self.construct_AuthorizationRequest(AuthorizationRequest,
                                                       request_args=args)

        location = areq.request(self.authorization_endpoint)

        if self.debug:
            _log_info("Redirecting to: %s" % location)

        return sid, location
Example #32
0
class ClaimsServer(Provider):
    def __init__(self,
                 name,
                 sdb,
                 cdb,
                 userinfo,
                 client_authn,
                 urlmap=None,
                 ca_certs="",
                 keyjar=None,
                 hostname="",
                 dist_claims_mode=None):
        Provider.__init__(self, name, sdb, cdb, None, userinfo, None,
                          client_authn, "", urlmap, ca_certs, keyjar, hostname)

        if keyjar is None:
            keyjar = KeyJar(ca_certs)

        for cid, _dic in cdb.items():
            try:
                keyjar.add_symmetric(cid, _dic["client_secret"],
                                     ["sig", "ver"])
            except KeyError:
                pass

        self.srvmethod = OICCServer(keyjar=keyjar)
        self.dist_claims_mode = dist_claims_mode
        self.info_store = {}
        self.claims_userinfo_endpoint = ""

    def _aggregation(self, info):

        jwt_key = self.keyjar.get_signing_key()
        cresp = UserClaimsResponse(jwt=info.to_jwt(key=jwt_key,
                                                   algorithm="RS256"),
                                   claims_names=info.keys())

        logger.info("RESPONSE: %s" % (cresp.to_dict(), ))
        return cresp

    #noinspection PyUnusedLocal
    def _distributed(self, info):
        # store the user info so it can be accessed later
        access_token = rndstr()
        self.info_store[access_token] = info
        return UserClaimsResponse(endpoint=self.claims_userinfo_endpoint,
                                  access_token=access_token,
                                  claims_names=info.keys())

    #noinspection PyUnusedLocal
    def do_aggregation(self, info, uid):
        return self.dist_claims_mode.aggregate(uid, info)

    #noinspection PyUnusedLocal
    def claims_endpoint(self, request, http_authz, *args):
        _log_info = logger.info

        ucreq = self.srvmethod.parse_user_claims_request(request)

        _log_info("request: %s" % ucreq)

        try:
            resp = self.client_authn(self, ucreq, http_authz)
        except Exception, err:
            _log_info("Failed to verify client due to: %s" % err)
            resp = False

        if "claims_names" in ucreq:
            args = dict([(n, {
                "optional": True
            }) for n in ucreq["claims_names"]])
            uic = Claims(**args)
        else:
            uic = None

        _log_info("User info claims: %s" % uic)

        #oicsrv, userdb, subject, client_id="", user_info_claims=None
        info = self.userinfo(ucreq["sub"],
                             user_info_claims=uic,
                             client_id=ucreq["client_id"])

        _log_info("User info: %s" % info)

        # Convert to message format
        info = OpenIDSchema(**info)

        if self.do_aggregation(info, ucreq["sub"]):
            cresp = self._aggregation(info)
        else:
            cresp = self._distributed(info)

        _log_info("response: %s" % cresp.to_dict())

        return Response(cresp.to_json(), content="application/json")
Example #33
0
    def get(self):
        self.create_client()

        # When we have a code, request id token
        if self.get_argument('code', False):
            response = self.request.query
            response = self.client.parse_response(
                AuthorizationResponse, info=response,
                sformat="urlencoded")

            # The query is checked against the state in the cookie
            code = response['code'] if 'code' in response else None
            state = response['state'] if 'state' in response else None
            cookie = self.get_secure_cookie(self.contest.name + "_auth")
            if code and cookie and json.loads(cookie)[0] == state:
                args = {
                    "code": code,
                    "token_endpoint": self.op_info["token_endpoint"],
                    "redirect_uri": self.redirect_uri,
                }
                # Request an access token to the authentication server
                resp = yield self.get_access_token(
                    state=state,
                    request_args=args,
                    authn_method="client_secret_basic"
                )
            else:
                self.redirect_login_error()

            self.clear_cookie(self.contest.name + "_auth")

            # The token is checked against the nonce in the cookie
            id_token = resp["id_token"]
            if id_token["nonce"] != json.loads(cookie)[1]:
                self.redirect_login_error()

            first_name = (
                id_token["given_name"]
                if "given_name" in id_token
                else "")
            last_name = (
                id_token["family_name"]
                if "family_name" in id_token
                else "")
            email = (
                id_token["email"]
                if "email" in id_token
                else "")
            username = id_token["sub"]

            # Check if the user already exists
            user = self.sql_session.query(User)\
                .filter(User.username == username).first()

            # Create the user if it doesn't exist yet
            if user is None:
                user = User(
                    first_name, last_name, username, email=email,
                    password=build_password(generate_random_password()))
                self.sql_session.add(user)
                self.sql_session.commit()
            if not [p for p in user.participations
                    if p.contest_id == self.contest.id]:
                participation = Participation(
                    contest=self.contest, user=user)
                self.sql_session.add(participation)
                self.sql_session.commit()

            self.try_user_login(user)

        # Request a code
        else:
            state = rndstr()
            nonce = rndstr()
            self.set_secure_cookie(
                self.contest.name + "_auth",
                json.dumps([state, nonce]),
                expires_days=None)
            claims_request = ClaimsRequest(
                id_token=Claims(
                    sub={"essential": True}
                ),
                userinfo=Claims(
                    given_name={"essential": True},
                    family_name={"essential": True},
                    preferred_username={"essential": True},
                )
            )
            args = {
                "client_id": self.client.client_id,
                "response_type": "code",
                "scope": ["openid", "offline_access"],
                "nonce": nonce,
                "redirect_uri": self.redirect_uri,
                "state": state,
                "claims": claims_request,
            }
            next_page = self.get_argument('next', None)
            if next_page:
                args["next"] = next_page

            auth_req = (self.client.construct_AuthorizationRequest
                        (request_args=args))
            login_url = auth_req.request(
                self.op_info["authorization_endpoint"])
            self.redirect(login_url)