Unauthenticated identites not mapping to Developer Authenticated Identity - ios

When a user launches the app they are an unauthenticated user and they receive a unauthenticated cognito identity supplied from the DeveloperAuthenticatedIdentityProvider class in the iOS app. I can see that unauthenticated cognito identity in the cognito console. However, when they login and I make a call to my nodejs backend with a logins map of:
{
DevAuthIdentityLogin:<username>
}
and using this backend code:
getCognitoIdentity: function(logins, cognitoId, error) {
var cognitoidentity = new AWS.CognitoIdentity(awsOptions);
var params = {
IdentityPoolId: identityPool,
Logins: logins,
TokenDuration: (60 * 5)
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params, function(err, data) {
if (err) {
console.log(err, err.stack);
error(err)
} else {
console.log(data); // successful response
cognitoId(data);
}
});
}
It creates a new identity id with the developer authenticated identity and I can see that in the cognito console, but the old unauthenticated one is not being mapped to this new developer authenticated one.
Do I need to supply the unauthenticated identity id in the logins map when making a call to my backend to associate the two? OR am I not making this call correctly. I need some clarification on how to map/merge these identities please.

I already answered your question on our forums, but you need to include the unauth identitity id as a parameter to the GetOpenIdTokenForDeveloperIdentity call, otherwise Amazon Cognito will have no way of knowing that it should associate your user identifier to that identity.

Related

iOS Google SignIn refreshed idToken has missing profile info in backend authentication

I use GoogleSignIn for iOS (GoogleSignIn-iOS), v6.1.0, in my iOS app.
All calls to my backend have the idToken in the request header.
The id token is verified in the backend. Here I also need to retrieve the users email and name.
(see also: https://developers.google.com/identity/sign-in/ios/backend-auth)
After a new SignIn with GIDSignIn.sharedInstance.signIn everything works fine.
GIDSignIn.sharedInstance.currentUser.profile contains email and name.
When sending the idToken to the backend, the Verifier gives me name and email in its payload, too.
Before I do a backend request, I get a valid (=not expired) idToken, with the following code:
private static func refreshToken(_ authentication: GIDAuthentication) async throws -> GIDAuthentication {
try await withCheckedThrowingContinuation { continuation in
authentication.do { authentication, error in
if let authentication = authentication {
continuation.resume(returning: authentication)
} else if let error = error {
Log.warn("Google SignIn refreshToken failed with -> \(error)")
continuation.resume(throwing: error)
}
}
}
}
I use the following code to get the idToken, before I create the request for my URLSession.
func idToken() async -> String {
do {
guard let user = GIDSignIn.sharedInstance.currentUser else {
Log.error("No GID user to get idToken from")
return ""
}
currentAuth = try await Self.refreshToken(user.authentication) //currentAuth is a class variable
return currentAuth?.idToken ?? ""
} catch {
print("Error during Google SignIn idToken retrieval \(error)")
return ""
}
}
And now my problem comes:
The idToken is refreshed properly. It is valid for another hour, and the verifier in my backend accepts it.
But I can't get the users name from the verified payload data in the backend, the name field is null.
Same happens when I use GIDSignIn.sharedInstance.restorePreviousSignIn (which I call on every app re-start, to do the silent sign in. (But in the app, the values are there in the updated users object profile)
It seems to me, that when the idToken gets refreshed, that it looses the profile scope.
I hope someone can help me with this, or at least explain the behaviour to me.
Thank in advance :)
Update
I checked the idTokens on https://jwt.io.
They are valid, but after the refresh, the jwt payload definitely is missing the profile data, like the users name.
I waited one day and tried again. Now the silent signin after app start gives me a complete idToken with jwt payload including name, but only once. After an hour, when the idToken gets refreshed, the idToken is again missing the profile information
Unfortunately I got no hint here, so I solved my problem as follows.
I hope this approach can save time for some others in the future.
I only require the profile data, when the user logs in to the backend the first time and a new user record is created in the backend.
In all other calls, where I need the JWT for authentication, I only rely on the basic information (ID, email) and handle all other values as optional values.
So I check the users name, if it is available. Otherwise the ID and a valid token is of course sufficent for authentication.

Re enter without password after revoke salesforce oauth token

I built a simple app that allows our customers to gather specific information from their Salesforce system. For authentication I used "WebServer Flow", which allows a user to log in to our site with his Salesforce account.
I should of course allow the user to log out of his account. But for some reason, even though I send a Revoke request to Salesforce and get an OK response, when the user redirected again to the Salesforce login page, it automatically logs in to the previous account without re-entering details.
this is the logout action in my backend,
public async Task<ContentResult> LogOutFromSalseforce(string code)
{
AuthenticationClient auth;
bool hasAuth = AuthSessionWrapper.AuthDic.TryGetValue(code, out auth);
if (!hasAuth) return Content(JsonConvert.SerializeObject(new { error = "session expired" }), "application/json");
var url = auth.InstanceUrl + _revokeEndpointUrl;
var cl = new HttpClient();
var res = await cl.PostAsync(url, new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("token", auth.RefreshToken) }));
AuthSessionWrapper.AuthDic.Remove(code);
return Content(JsonConvert.SerializeObject(new { success = res.StatusCode == HttpStatusCode.OK }), "application/json");
}
And after call from client to this action he redirected to our login page.
I found that even if i call to this revoke endpoint from client it's not work.
only if the user enter in another tab to Salesforce and click logout there, he need to re enter his details to login again to our site.
What I'm doing wrong?
If someone get into the same problem, I solved it with add "&prompt=login" to the login redirect url.

How to authenticate with Apple SSO (TV Provider) using VideoSubscriberAccount framework

I need to implement authentication scheme using Apple SSO for my application:
Check for an signed user in Settings TV Provider
Sing in into TV Provider from my app if there is no a signed user
Get authentication payload of a signed user from my provider backend with tokens, uuid, etc.
What the main steps to config and implement Apple SSO authentication using VideoSubscriberAccount framework because unfortunately there is no much info and samples about?
There are several common steps to implement authentication scheme with Apple SSO:
1. Config your provisional profile, .entitlements and Info.plist files
YourApp.entitlements file must have a special key that enables SSO for your app:
com.apple.developer.video-subscriber-single-sign-on Boolean YES
This entitlement also should be present in your provisional profile e.g.:
Info.plist must have next key with a message that will be shown to user on first access to the video subscriptions:
NSVideoSubscriberAccountUsageDescription String "This app needs access to your TV Provider."
2. Create an account manager instance and implement delegate methods to coordinate access to a subscriber's account.
import VideoSubscriberAccount
...
let accountManager = VSAccountManager()
accountManager.delegate = self
...
extension YourController : VSAccountManagerDelegate {
func accountManager(_ accountManager: VSAccountManager, present viewController: UIViewController) {
window?.rootViewController?.present(viewController, animated: true, completion: nil)
}
func accountManager(_ accountManager: VSAccountManager, dismiss viewController: UIViewController) {
viewController.dismiss(animated: true, completion: nil)
}
func accountManager(_ accountManager: VSAccountManager, shouldAuthenticateAccountProviderWithIdentifier accountProviderIdentifier: String) -> Bool {
return true
}
}
3. Determine the state of the application's access to the user's subscription information.
accountManager.checkAccessStatus(options: [VSCheckAccessOption.prompt : true]) { status, error in
...
}
If the app tries to access to subscription information first time next prompt will be shown:
4. Request information about the subscriber's account.
If access is granted you can make a metadata request to check for a signed user:
if case .granted = status {
let request = VSAccountMetadataRequest()
request.includeAccountProviderIdentifier = true
request.isInterruptionAllowed = true
accountManager.enqueue(request) { metadata, error in
...
}
}
The provider selection list and sign-in form will be shown if there is no signed account:
To skip providers list view you can set supported providers identifiers to the request e.g.:
request.supportedAccountProviderIdentifiers = ["Hulu"]
5. Second metadata request with required attributes from a identity provider's info endpoint
If there is no error and signed account is present you should call to your identity provider's info endpoint to obtain required attributes for second metadata call such as:
attributeNames: a list of SAML attributes needed
verificationToken: Base64 encoded signed authentication request from the service provider to the identity provider
channelIdentifier: service provider entity id
And make second request with these parameters:
request.attributeNames = attributeNames
request.verificationToken = verificationToken
request.channelIdentifier = channelIdentifier
accountManager.enqueue(request) { metadata, error in
...
}
6. Request translation of SAML authentication response
The seconds metadata request responds with Apple's SAML payload that should be sent to your identity provider's translation endpoint then the endpoint parses that element and returns a response that resembles the authentication payload e.g.:
if let samlPayload = metadata?.samlAttributeQueryResponse {
let body = [
...
"saml" : samlPayload
]
fetch("https://your.identity.provider/saml/translate", httpBody: body)
}
The endpoint should respond with JSON which contains all your authentication data: tokens, uuid etc.

Swift - Firebase Auth with Microsoft Graph (Redirect URL Problem)

I'm having a problem integrating Firebase with Microsoft Auth in my iOS App.
The login page has been launched and I can sign in by Office365 account but login auth can not be finished because of the below Error :
"AADSTS50011: The reply url specified in the request does not match the reply urls configured for the application:[app-id]"
I did check the setting in Firebase and below are the settings I add in the app of Azure Active Directory :
Web redirect URL : "*.firebaseapp.com/__/auth/handler"
Supported account types : "Accounts in any organizational directory (Any Azure AD directory Multitenant)"
Here are the swift code I implement :
provider = OAuthProvider(providerID: "microsoft.com")
provider?.customParameters = ["prompt": "consent",
"login_hint": "Login Hint"]
provider?.scopes = ["mail.read", "calendars.read"]
provider?.getCredentialWith(_: nil){ (credential, error) in
if let credential = credential {
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print(error.localizedDescription)
}
}
}
}
Does anyone know how to solve the problem or have the same problem?
When registering apps with these providers, be sure to register the
*.firebaseapp.com domain for your project as the redirect domain for your app.
Have you replaced * with your projectName? You can find your Authorized Domain under Authorized Domains in firebase console. The redirect uri should be something like
https://yourFirebaseApp.firebaseapp.com/__/auth/handler

How to force Google OAuth popup in Firebase when user already authenticated previously?

Whenever a user has previously authenticated with Google, it automatically defaults to logging them in with THAT account on subsequent attempts. I want to eliminate this and force the popup/redirect so that a user with multiple google accounts can choose which one to use. How?
Background:
The automatic logging in feature is proving problematic for me as I have a whitelisted set of e-mails for users allowed to use my app. If a Google user chooses the wrong account when first logging in, they can't go back and choose the one associated to their whitelisted e-mail.
Just as #nvnagr said, you can do this with the following code:
var provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
'prompt': 'select_account'
});
But I think you need to update the firebase version to 3.6.0 something.
Google supports a parameter in authentication url to deal with this issue.
If you add prompt=select_account in your request to Google authentication, it'll force the user to do an account selection. See the details and other values of prompt.
https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
I'm not sure if there is an easy way to add this parameter through firebase api.
When you're calling the oAuth function, you can pass a third options parameter to make the authentication last for the session only. This should solve your problem. Docs
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.authWithOAuthPopup("google", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
}, {
remember: 'sessionOnly'
});

Resources