def verify_authentication(resolver_client: ResolverClient, token: str) -> dict: """ Verify if the authentication token is allowed for authentication. :param resolver_client: resolver client interface :param token: jwt authentication token :return: decoded verified authentication token :raises: IdentityAuthenticationFailed: if not allowed for authentication """ try: unverified_token = JwtTokenHelper.decode_token(token) for field in ('iss', 'sub', 'aud', 'iat', 'exp'): if field not in unverified_token: raise IdentityValidationError(f'Invalid token, missing {field} field') issuer = Issuer.from_string(unverified_token['iss']) doc = resolver_client.get_document(issuer.did) get_controller_doc = resolver_client.get_document issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_auth(doc, issuer.name, get_controller_doc) if not issuer_key: raise IdentityInvalidRegisterIssuerError(f'Invalid issuer {issuer}') verified_token = JwtTokenHelper.decode_and_verify_token(token, issuer_key.public_key_base58, unverified_token['aud']) IdentityAuthValidation.validate_allowed_for_auth(resolver_client, issuer_key.issuer, verified_token['sub']) return {'iss': verified_token['iss'], 'sub': verified_token['sub'], 'aud': verified_token['aud'], 'iat': verified_token['iat'], 'exp': verified_token['exp']} except (IdentityValidationError, IdentityResolverError, IdentityInvalidRegisterIssuerError, IdentityNotAllowed) as err: raise IdentityAuthenticationFailed('Not authenticated') from err
def from_challenge_token(resolver_client: ResolverClient, challenge_token: str) -> 'Proof': """ Build proof from challenge token. :param resolver_client: resolver client to get the registered documents :param challenge_token: jwt challenge token :return: valid proof :raises: IdentityValidationError: if invalid challenge token """ decoded_token = JwtTokenHelper.decode_token(challenge_token) iss = decoded_token.get('iss') aud = decoded_token.get('aud') if not iss or not aud: raise IdentityValidationError( 'Invalid challenge token, missing \'iss\' or \'aud\'') issuer = Issuer.from_string(iss) doc = resolver_client.get_document(issuer.did) get_controller_doc = resolver_client.get_document issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_control_only( doc, issuer.name, get_controller_doc) if not issuer_key: raise IdentityInvalidRegisterIssuerError( f'Invalid issuer {issuer}') verified_token = JwtTokenHelper.decode_and_verify_token( challenge_token, issuer_key.public_key_base58, aud) return Proof(issuer_key.issuer, aud.encode('ascii'), verified_token['proof'])
def get_valid_doc_from_token( token: str, get_controller_doc: GetControllerDocFunc) -> RegisterDocument: """ Get a valid RegisterDocument from a resolver token. :param token: resolver token :param get_controller_doc: get controller register document function :return: valid register document :raises: IdentityResolverError: if invalid token IdentityResolverError: if invalid document """ try: unverified = JwtTokenHelper.decode_token(token) doc = RegisterDocumentBuilder().build_from_dict(unverified['doc']) issuer = Issuer.from_string(unverified['iss']) issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_control_only( doc, issuer.name, get_controller_doc) if not issuer_key: raise IdentityInvalidRegisterIssuerError( f'Invalid issuer {issuer}') JwtTokenHelper.decode_and_verify_token( token, issuer_key.public_key_base58, unverified['aud']) return doc except (KeyError, ValueError, IdentityValidationError, IdentityInvalidRegisterIssuerError) as exc: raise IdentityResolverError( f'Can not deserialized invalid resolver token: \'{exc}\'' ) from exc
def build_from_dict(self, data: dict) -> RegisterDocument: """ Build a valid immutable register document from dict. :param data: register document as dict :return: valid register document :raises: IdentityInvalidDocumentError: if invalid dict data IdentityInvalidDocumentError: if invalid document IdentityRegisterDocumentKeyConflictError: if key name is not unique """ try: for k in data['publicKey']: self.add_public_key_obj(RegisterPublicKey.from_dict(k)) for k in data.get('authentication', []): self.add_authentication_key_obj(RegisterAuthenticationPublicKey.from_dict(k)) for k in data.get('delegateAuthentication', []): self.add_authentication_delegation_obj(RegisterDelegationProof.from_dict(k)) for k in data.get('delegateControl', []): self.add_control_delegation_obj(RegisterDelegationProof.from_dict(k)) raw_controller = data.get('controller') controller = Issuer.from_string(raw_controller) if raw_controller else None return self.build(data['id'], DIDType(data['ioticsDIDType']), data['proof'], data.get('revoked', False), Metadata.from_dict(data.get('metadata', {})), data.get('creator'), data['ioticsSpecVersion'], data['updateTime'], controller) except (TypeError, KeyError, ValueError) as err: raise IdentityInvalidDocumentError(f'Can not parse invalid register document: \'{err}\'') from err
def auth_deleg_proof(): issuer1 = Issuer.from_string( 'did:iotics:iotHHHHKpPGWyEC4FFo4d6oyzVVk6MXLmEgY#ctrl1') issuer2 = Issuer.from_string( 'did:iotics:iotHHHHKpPGWyEC4FFo4d6oyzVVk6MXLmEgY#ctrl2') issuer3 = Issuer.from_string( 'did:iotics:iotHHHHKpPGWyEC4FFo4d6oyzVVk6MXLmEgY#ctrl3') return { '#DelegAuthKey1': RegisterDelegationProof.build('#DelegAuthKey1', issuer1, proof='proof', revoked=False), '#DelegAuthKey2': RegisterDelegationProof.build('#DelegAuthKey2', issuer2, proof='proof', revoked=False), '#DelegAuthKey3': RegisterDelegationProof.build('#DelegAuthKey3', issuer3, proof='proof', revoked=False), }
def from_dict(data: dict): """ Build a register delegation public key from dict. :param data: register delegation public key as dict :return: valid register delegation key :raises: IdentityValidationError: if invalid register delegation public key as dict """ try: controller = Issuer.from_string(data['controller']) proof_type = DelegationProofType(data.get('proofType', DelegationProofType.DID.value)) return RegisterDelegationProof.build(data['id'], controller, data['proof'], data.get('revoked', False), proof_type) except (TypeError, KeyError, ValueError) as err: raise IdentityValidationError(f'Can not parse invalid register delegation proof: \'{err}\'') from err
def test_can_build_issuer_from_string_with_invalid_string_raises_validation_error(did): with pytest.raises(IdentityValidationError): Issuer.from_string(f'{did}aNameWithoutSep')
def test_building_issuer_from_string_with_invalid_did_raises_validation_error(name): with pytest.raises(IdentityValidationError): Issuer.from_string(f'invalidDID{name}')
def test_building_issuer_from_string_with_invalid_name_raises_validation_error(did): with pytest.raises(IdentityValidationError): Issuer.from_string(f'{did}#invalid name')
def test_can_build_issuer_from_string(did, name): issuer_string = f'{did}{name}' issuer = Issuer.from_string(issuer_string) assert issuer.did == did assert issuer.name == name
def test_can_build_a_register_doc_from_an_other_doc_overriding_values( full_doc): existing_doc = full_doc new_creator = 'did:iotics:iotEEEEKpPGWyEC4FFo4d6oyzVVk6MEEEEgY' new_controller = Issuer.build( 'did:iotics:iotEEEEKpPGWyEC4FFo4d6oyzHHHHMEEEEgY', '#NewController') new_metadata = Metadata(label='a label') new_version = SUPPORTED_VERSIONS[0] new_pub_key = RegisterPublicKey(name='#new_pub_key1', base58=get_public_base_58_key(), revoked=False) new_auth_key = RegisterAuthenticationPublicKey( name='#new_auth_key1', base58=get_public_base_58_key(), revoked=False) a_controller = Issuer.from_string( 'did:iotics:iotHjrmKpPGWyEC4FFo4d6oyzVVk6MXEEEEE#AController') new_control_deleg_proof = RegisterDelegationProof( name='#new_deleg_control_key1', controller=a_controller, proof='a_deleg_proof_validated_by_the_resolver', revoked=False) new_auth_deleg_key = RegisterDelegationProof( name='#new_deleg_auth_key1', controller=a_controller, proof='a_deleg_proof_validated_by_the_resolver', revoked=False) new_doc = RegisterDocumentBuilder() \ .add_public_key_obj(new_pub_key) \ .add_authentication_key_obj(new_auth_key) \ .add_control_delegation_obj(new_control_deleg_proof) \ .add_authentication_delegation_obj(new_auth_deleg_key) \ .build_from_existing(existing_doc, revoked=True, metadata=new_metadata, creator=new_creator, spec_version=new_version, controller=new_controller) # Can not change assert new_doc.did == existing_doc.did assert new_doc.purpose == existing_doc.purpose assert new_doc.proof == existing_doc.proof # Overridden values assert new_doc.revoked assert new_doc.metadata == new_metadata assert new_doc.creator == new_creator assert new_doc.spec_version == new_version assert new_doc.controller == new_controller assert new_doc.public_keys == { **existing_doc.public_keys, **{ new_pub_key.name: new_pub_key } } assert new_doc.auth_keys == { **existing_doc.auth_keys, **{ new_auth_key.name: new_auth_key } } assert new_doc.control_delegation_proof == { **existing_doc.control_delegation_proof, **{ new_control_deleg_proof.name: new_control_deleg_proof } } assert new_doc.auth_delegation_proof == { **existing_doc.auth_delegation_proof, **{ new_auth_deleg_key.name: new_auth_deleg_key } } assert new_doc.update_time >= existing_doc.update_time