Self provides a secure authentication workflow that allows users to authenticate themselves using their digital identity. This workflow is designed to be scalable and can be used in a variety of scenarios, such as:
Once we have the user's address (userAddress), we can request specific credentials from them. In this example, we're requesting a liveness credential. For more details on credential presentation requests, check out the Credential Presentation guide.
You need to listen to the request in the account’s callback and then check the incoming message for its type and details. If the request details are correct, open the Liveness UI flow. After receiving the liveness credentials, send the response back.
varcredentialRequest:CredentialRequest? =nullaccount.setOnRequestListener{msg->when(msg){isCredentialRequest->{if(msg.details().any{it.types().contains(CredentialType.Liveness)&&it.subject()==Constants.SUBJECT_SOURCE_IMAGE_HASH}){credentialRequest=msgnavController.navigate("livenessRoute")// launch liveness UI flow}}}}// integrate Liveness UI flow into NavigationaddLivenessCheckRoute(navController,route="livenessRoute",selfModifier=selfModifier,account={account},withCredential=true,onFinish={selfie,credentials->if(credentialRequest!=null){// after receiving the liveness credentials, send the responsesendCredentialResponse(credentials)}})funsendCredentialResponse(credentials:List<Credential>){valcredentialResponse=CredentialResponse.Builder().setRequestId(credentialRequest.id()).setTypes(credentialRequest.types()).setToIdentifier(credentialRequest.toIdentifier()).setFromIdentifier(credentialRequest.fromIdentifier()).setStatus(ResponseStatus.accepted).setCredentials(credentials).build()account.send(credentialResponse)}
// 1. Handle credential request in your request listeneraccount.setOnRequestListener{[weakself]requestMessageinguardletself=selfelse{return}ifletcredentialRequest=requestMessageas?CredentialRequest{print("Credential request received from: \(credentialRequest.fromIdentifier())")Task{@MainActorinawaitself.handleCredentialRequest(credentialRequest)}}}// Check credential request type to process to right flows// 2. Handle credential request with proper filtering and thread safetyprivatefunchandleCredentialRequest(_credentialRequest:CredentialRequest)async{// Check all requested credentials - filter for liveness credentialsletallClaims:[Claim]=credentialRequest.details()letlivenessCredentials=allClaims.filter{$0.types().contains(CredentialType.Liveness)&&$0.types().contains(CredentialType.Verifiable)}letotherCredentials=allClaims.filter{!($0.types().contains(CredentialType.Liveness)&&$0.types().contains(CredentialType.Verifiable))}print("Total credentials requested: \(allClaims.count)")print("Liveness credentials: \(livenessCredentials.count)")print("Other credentials: \(otherCredentials.count)")// Log other credential types for debuggingforclaiminotherCredentials{print("Ignoring unsupported credential types: \(claim.types())")}if!livenessCredentials.isEmpty{print("✅ Processing liveness credentials only")awaithandleLivenessAuthentication(credentialRequest,livenessCredentials:livenessCredentials)}else{print("❌ No liveness credentials found - this app only supports liveness authentication")}}// This is an example to show Liveness Authentication// 3. Handle liveness authentication with proper thread managementprivatefunchandleLivenessAuthentication(_credentialRequest:CredentialRequest,livenessCredentials:[Claim])async{// Analyze liveness requirements from filtered credentialsforclaiminlivenessCredentials{letclaimTypes=claim.types()letsubject=claim.subject()letcomparisonOperator=claim.comparisonOperator()print("Processing liveness claim: types=\(claimTypes), subject=\(subject), operator=\(comparisonOperator)")}// Present liveness check flow on main thread (critical for UI)SelfSDK.showLiveness(account:account,showIntroduction:false,autoDismiss:true){[weakself]selfieImageData,credentials,erroringuardletself=selfelse{return}ifleterror=error{print("Liveness check failed: \(error)")return}guard!credentials.isEmptyelse{print("No credentials provided from liveness check")return}// Send credential responseTask{@MainActorinawaitself.sendCredentialResponse(to:credentialRequest,credentials:credentials)}}}// Response to the above credential request// 4. Send credential response with proper type conversionprivatefuncsendCredentialResponse(torequest:CredentialRequest,credentials:[Any])async{// Convert credentials to proper type (critical for Swift type safety)letselfCredentials=credentials.compactMap{$0as?Credential}guard!selfCredentials.isEmptyelse{print("No valid credentials to send")return}do{letresponse=CredentialResponse.Builder().withRequestId(request.id()).withTypes(request.types()).toIdentifier(request.toIdentifier()).withStatus(ResponseStatus.accepted).withCredentials(selfCredentials).build()tryawaitaccount.send(message:response){messageId,errorinifleterror=error{print("❌ Response failed: \(error)")}else{print("✅ Credentials sent successfully")}}}catch{print("Error building credential response: \(error)")}}
After receiving the credential presentation response, we need to validate the credentials. This involves checking the presentation's validity, the credential's validity, and extracting the claims. For more information on credential validation, see the Credential Validation guide.
for_,credential:=rangepresentation.Credentials(){ifcredential.CredentialType()[0]=="VerifiableCredential"&&credential.CredentialType()[1]=="LivenessCredential"{err=credential.Validate()iferr!=nil{log.Printf("WARN: failed to validate credential - error: %v",err)continue}ifcredential.ValidFrom().After(time.Now()){log.Println("WARN: credential is intended to be used in the future")continue}claims,err:=credential.CredentialSubjectClaims()iferr!=nil{log.Printf("WARN: failed to parse credential claims - error: %v",err)continue}fork,v:=rangeclaims{log.Printf("INFO: credential value - credentialType: %s, field: %s, value: %v",credential.CredentialType(),k,v)}}}
This example demonstrates how to build a simple secure authentication workflow with Self. We've seen how to use Self Discovery to find the DIDs of the users we want to authenticate, how to send a credential presentation request for the appropriate credentials, and how to validate the presentations and credentials.
For the complete example, including error handling and additional details, please refer to our GitHub repository.
By leveraging these Self features, you can create a robust, secure, and privacy-preserving authentication system for your applications.