How to renew the id token using auth0 in SwiftUI app - swift

I'm using Auth0 for login and logout in my iOS app. after the user logs in I get an id token which I use to make the further api calls in my app. we need to keep updating the token with auth0 as mentioned in their doc
My function is as follows
struct UpdateToken {
let credentialsManager: CredentialsManager
init() {
self.credentialsManager = CredentialsManager(authentication: Auth0.authentication())
}
func updateToken() {
guard credentialsManager.canRenew() else {
// Present login screen
print("not renewing")
return
}
Auth0
.webAuth()
.scope("openid profile offline_access")
.audience("\(audience)/userinfo")
.start {
switch $0 {
case .failure(let error):
print("token update failed")
break
// Handle error
case .success(let credentials):
// Pass the credentials over to the Credentials Manager
credentialsManager.store(credentials: credentials)
UserDefaults.standard.set(credentials.idToken, forKey: "id_token")
print("token updated")
}
}
}
}
it is printing not renewing in my console. I'm not sure what I am missing here.
the login function works perfectly fine
func login() {
Auth0
.webAuth()
.start { result in
// Handle the result of the authentication
switch result {
case .failure(let error):
// If the authentication fails, print the error message
print("Failed with: \(error)")
case .success(let credentials):
// If the authentication is successful, store the credentials and user information in UserDefaults
self.userProfile = Profile.from(credentials.idToken)
self.userIsAuthenticated = "1"
print("Credentials: \(credentials)")
// Store the ID token
print("ID token: \(credentials.idToken)")
UserDefaults.standard.set(credentials.idToken, forKey: "id_token")
// Print and store the token type and access token
print("token type: \(credentials.tokenType)")
print("access token \(credentials.accessToken)")
// Extract and store the user ID, name, and email from the user profile
print("userID is \(userProfile.id)")
let fullString = userProfile.id
let parts = fullString.split(separator: "|")
let desiredPart = String(parts[1])
print(desiredPart)
UserDefaults.standard.set(desiredPart, forKey: "userId")
UserDefaults.standard.set(userProfile.name, forKey: "userName")
UserDefaults.standard.set(userProfile.email, forKey: "userEmail")
}
}
}

It sounds like canRenew() is unable to find any stored credentials - Try using credentialsManager.store on initial login similar to how you are in updateToken(). This way the credentials are stored in the keychain when a user logs in to begin with.

Related

Trying to access https://api.github.com/user but it is returning status code of 401

I am working on creating Github third-party OAuth application. I get an email like this, which shows that my app is authorized:
A third-party OAuth application (Dev Snapshot) with read:user,
repo:status, user:email, and user:follow scopes was recently
authorized to access your account.
I am also signed in under my Github account via Firebase. However, I cannot access this endpoint: https://api.github.com/user
I get 401 response, meaning I am not authorized.
Here is the code that signs me in my app:
provider.customParameters = [
"allow_signup": "true"
]
provider.scopes = ["read:user","user:email","user:follow","repo:status"]
provider.getCredentialWith(nil) { credential, error in
if error != nil {
print("error: \(error!.localizedDescription)")
}
if credential == nil {
print("no credential")
}
if credential != nil {
Auth.auth().signIn(with: credential!) { [self] authResult, error in
if error != nil {
// Handle error.
print("error sign in: \(error?.localizedDescription)")
}
// User is signed in.
// IdP data available in authResult.additionalUserInfo.profile.
guard let oauthCredential = authResult?.credential as? OAuthCredential else {
print("oauth error")
return
}
print("signed in")
// GitHub OAuth access token can also be retrieved by:
// oauthCredential.accessToken
// GitHub OAuth ID token can be retrieved by calling:
// oauthCredential.idToken
configure()
tableView.reloadData()
}
}
}

AWSUserPoolsSignIn refresh token iOS

I am using the swift package AWSiOSSDKV2 from https://github.com/aws-amplify/aws-sdk-ios-spm
I can log in using the SDK but how can I use refresh Token to generate new accessToken using their SDK?
func login() {
let pool = AWSCognitoIdentityUserPool(forKey: PortalUserConfig.POOL_NAME)
if let user = pool?.getUser() {
user.getSession(username, password: password, validationData: nil).continueWith(block: { task -> Any? in
if let error = task.error {
let errorMessage = (error as NSError).userInfo["message"] as? String
} else {
guard let accessToken = task.result?.accessToken else {
return nil
}
print(accessToken)
let refreshToken = task.result?.refreshToken
print(refreshToken)
}
return nil
})
}
}
The Amplify library will automatically fetch new id and access tokens if they have expired and the refresh token is still valid. You can adjust the lifetime of your refresh tokens in the settings for your User Pool client. To learn more about OAuth token lifetime strategies, you can read up on the options over at OAuth.com.

Swift/Firebase - Check if user is logged in via Facebook & Google

I use CurrentSession environment object to store user info. A user can login into the app via Google or Facebook. I wanna keep the user logged in even after the app is closed and opened again. So I was going to check provider specific objects/variables to perform these checks. Works well for FB but unfortunately doesn't work for Google (!)
So I login via Facebook or Google and close the app..
When I launch my app again the CurrentSession is initialised. I check if the user was already logged in and it works well for Facebook by performing AccessToken.current != nil check.
But once I do the same for the user logged in via Google, when i relaunch the application GIDSignIn.sharedInstance()?.currentUser is always nil ;(
How should I perform this check for Google user? I know i can use Auth.auth() but at this moment I am looking for the way to do that via GIDSignIn object..
import Combine
import FirebaseCore
import FirebaseAuth
import GoogleSignIn
import FBSDKLoginKit
import FBSDKCoreKit
class CurrentSession: ObservableObject {
#Published var userId: String? = nil
init() {
// check if user logged via Facebook
if AccessToken.current != nil { <-- WORKS WELL FOR FACEBOOK
let credential = FacebookAuthProvider.credential(withAccessToken: AccessToken.current!.tokenString)
Auth.auth().signIn(with: credential) { (res,er) in
if er != nil{
print((er?.localizedDescription)!)
return
}
print("email: \(String(describing: res?.user.email))")
print("name: \(String(describing: res?.user.displayName))")
DispatchQueue.main.async {
self.userId = String(describing: res?.user.displayName)
}
}
}
// check if user logged via Google
if let user = GIDSignIn.sharedInstance()?.currentUser, let authentication = user.authentication { <<-- HERE (CHECK DOESN'T WORK AS I EXPECT)
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (res, err) in
if err != nil {
print("\(String(describing: err?.localizedDescription))")
return
}
print("email: \(String(describing: res?.user.email))")
print("name: \(String(describing: res?.user.displayName))")
DispatchQueue.main.async {
self.userId = String(describing: res?.user.displayName)
}
}
} else {
print("No current user found")
}
}
}
First thing, make sure to import FirebaseAuth.
Then you'll may want to create some enum:
enum AuthProviders: String {
case password
case phone
case facebook = "facebook.com"
case google = "google.com"
case apple = "apple.com"
}
Then perform this check to determine the current provider that used to sign-in:
if let providerId = Auth.auth().currentUser?.providerData.first?.providerID,
let provider = AuthProviders(rawValue: providerId){
switch provider {
case .password:
// Signed-in with Firebase Password
case .phone:
// Signed-in with Firebase Phone
case .google:
// Signed-in with Google
case .facebook:
// Signed-in with Facebook
case .apple:
// Signed-in with Apple
}
}
*if your app uses other provider(s), you may get its ProviderID from FIRAuthProvider.m and add it to the enum
*Note that anonymous sign-in providerId's will be nil as of this PR
It seems like i found a solution.
Here's the code that makes these checks:
if let signIn = GIDSignIn.sharedInstance(), signIn.hasPreviousSignIn() {
signIn.restorePreviousSignIn()
DispatchQueue.main.async {
self.userId = String(describing: GIDSignIn.sharedInstance()?.currentUser.userID)
print("userId = self.userId")
}
} else {
print("ATTEMPT FAILED")
}
}

Swift AWSMobileClient getTokens not working after federatedSignIn

AWSMobileClient.default().federatedSignIn(providerName: IdentityProvider.facebook.rawValue, token: (result?.token!.tokenString)!, completionHandler: { (userState, error) in
if let error = error{
print("Initialize Error: ", error.localizedDescription)
}else{
print("User State: ", userState!)//User State: signedIn
switch (userState) {
case .signedIn?:
DispatchQueue.main.async {
self.getAWSToken()
}
default:
print("Sign In needs info which is not et supported.")
}
}
})
func getAWSToken(){
AWSMobileClient.default().getTokens { (result, error) in
if error == nil{
print("JWT Token: ", (result?.idToken?.tokenString!)!)
print("Expire : ", (result?.expiration!)!)
AppData.sharedInstance.JWT_Token = (result?.idToken?.tokenString!)! as String
AppData.sharedInstance.Refresh_Token = (result?.refreshToken?.tokenString!)! as String
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd H:mm:ss"
let dtStr = dateFormatter.string(from: (result?.expiration!)!)
let expireDate = Utility.UTCToLocal(date: dtStr)
AppData.sharedInstance.JWT_Token_Expire_Date = expireDate
UserDefaults.standard.set(AppData.sharedInstance.JWT_Token_Expire_Date, forKey: Utility.KEY_JWT_TOKEN_EXPIRE_DATE)
UserDefaults.standard.set(AppData.sharedInstance.JWT_Token, forKey: Utility.KEY_JWT_Token)
UserDefaults.standard.set(AppData.sharedInstance.Refresh_Token, forKey: Utility.KEY_Refresh_Token)
DispatchQueue.main.async {
self.getUserInfo()
RSApiManager.shared.postDeviceToken(dToken: Utility.MY_TOKEN)
}
}else{
print("Error: ", error.debugDescription)
}
}
}
AWSMobileClient.default().getTokens always shows
"Error:
Optional(AWSMobileClient.AWSMobileClientError.notSignedIn(message:
"User is not signed in, please sign in to use this API."))"
even it shows success "User State: signedIn" for federateSignIn.
AWSMobileClient.default().signIn(username: username, password: password) { (signInResult, error) in
self.getAWSToken()
}
It is working fine after signIn() but not for federateSignIn().
This is expected behavior in your use-case.
If you want JsonWebTokens from the Cognito UserPool, you have to use a non-federated authentication method like .showSignIn() or .signIn(); those operate directly against the Cognito UserPool.
A Federated Identity provider can not get you Cognito JsonWebTokens because those tokens come directly from the Cognito UserPool; they don't come from something like the Cognito IdentityPool.
AWS's Amplify Documentation states:
...if the user is signed in via .federatedSignIn to a Cognito IdentityPool, only .identityId and AWS credentials for authenticated role will be available, and .getTokens() will return AWSMobileClientError.notSignedIn error.
This excerpt is under the documentation's heading of 'State Tracking > signedIn' 👈 found here.

AWS Cognito: developer authenticated identities problem in log in and refresh token

I implemented a social network app that uses Cognito for refreshing token. But still, I can't get my new tokens. When I first log-in to my server and get my first ID and token from it, The token expires after a while and I can't get any token. This is my implementation:
class DeveloperAuthenticatedIdentityProvider : AWSCognitoCredentialsProviderHelper {
override func token() -> AWSTask<NSString> {
self.identityId = ProfileDAL.shared.getId()
return AWSTask(result: NSString(string: ProfileDAL.shared.getToken()))
}
override func logins() -> AWSTask<NSDictionary> {
return super.logins()
}
}
I put this lines in my viewDidLoad right after getting first token from my server
let devAuth = DeveloperAuthenticatedIdentityProvider(regionType: MY_REGION, identityPoolId: MY_IDENTITY_POOL_ID, useEnhancedFlow: true, identityProviderManager:nil)
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: MY_REGION, identityProvider:devAuth)
let configuration = AWSServiceConfiguration(region: MY_REGION, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
and right after them, I use bellow lines to get my tokens:
AWSMobileClient.default().getTokens { (tokens, error) in
if let error = error {
print("Error getting token \(error.localizedDescription)")
} else if let tokens = tokens {
print(tokens.accessToken!.tokenString!)
}
}
finally I can't get my new tokens and it gives me this error:
AWSMobileClientError
â–¿ notSignedIn : 1 element
- message : "User is not signed in, please sign in to use this API."