Unauthenticated user to authenticated user on AWS Cognito - ios

My code is here: code
Reposted because I wanted to ask a more direct question. How do I switch between unauthenticated user and authenticated? My unauthenticated seems cached and I've used these methods:
[credentialsProvider clearCredentials];
[credentialsProvider clearKeychain];
before the rest of my api code and it still doesn't work. Any help is appreciated
Note: I know it's not working because I make a call using lambda right after I switch up my configuration/credentials provider and only authorized users should be able to call this method.
EDIT #behrooziAWS answer:
API CODE:
id<AWSCognitoIdentityProvider> identityProvider = [[DeveloperIdentityProviderClass alloc] initWithRegionType:AWSRegionUSEast1
identityId:nil
identityPoolId:#"SOMEIDENTITYPOOLID"
logins:#{#"MYIDENTITYPROVIDERNAME": #"MYUSERNAME"}
providerName:#"MYIDENTITYPROVIDERNAME"
];
[credentialsProvider setIdentityProvider:identityProvider];
[credentialsProvider setLogins:#{#"MYIDENTITYPROVIDERNAME": #"MYUSERNAME"}];
[[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){
[self testAuth];
return [BFTask taskWithResult:nil];
}];
Full Error:
BusyTime[27043:7097936] AWSiOSSDKv2 [Verbose] AWSURLResponseSerialization.m line:87 | -[AWSJSONResponseSerializer responseObjectForResponse:originalRequest:currentRequest:data:error:] | Response body: [{"message":"The security token included in the request is invalid."}]
2015-10-20 08:51:17.280 BusyTime[27043:7097936] Error: Error Domain=com.amazonaws.AWSLambdaErrorDomain Code=0 "The operation couldn’t be completed. UnrecognizedClientException" UserInfo=0x7ff27ab41150 {NSLocalizedFailureReason=UnrecognizedClientException, responseStatusCode=403, message=The security token included in the request is invalid., responseHeaders={type = immutable dict, count = 6,
IMPORTANT EDIT: I've hardcoded my refresh to use a working token and identityId. so:
self.identityId = #"someID";
self.token = #"someToken";
return [super getIdentityId];
and then ALL my code is working. But obviously this isn't sustainable, I need to be able to make a call to aws lambda to refresh my credentials. But when I set my identity provider, and set my login, I think it's changing me to my authenticated version, but I need to be in unauthenticated to call aws lambda. Please refer to my code link above and take a look at my refresh method to understand what I'm poorly trying to describe. Also please let me know if this should go in a new thread as this is a slightly different question. Not so familiar with stackoverflow's policies on questions.
Another error: [{"Message":"User: arn:aws:sts::445291524102:assumed-role/Cognito_BusyTimeAuth_Role/CognitoIdentityCredentials is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:445291524102:function:login"}], SO now I'm assuming my auth provider for my refresh which is incorrect login flow. I'm thinking that I switch this up so that I login in my API class. When I return my identity ID and token, I save them to keychain. Finally, I use the above API code to switch my logins and in my refresh method, I simply return what I found in my keychain. The only problem is I'm not sure if this flow is correct because it doesnt actually "refresh" as I'm not calling my backend. I was wondering if I could wrap the refresh by changing back and forth from my unauth role to my auth role but this seems messy.

[credentialsProvder clearKeychain] will clear the identityId, credentials and any logins, so clearCredentials is unnecessary: clearKeychain Documentation
Normally you don't want to clear your identity id when you transition to an authenticated user. If you simply add your provider and valid login token to the logins map and call [credentialsProvider refresh], you will become authenticated with the same identity id. From that point forward, you will only be able to access that identity if you provide a valid login token. If you want to switch identities by logging out and then login as a authenticated user, that is when you use clearKeychain.

Related

New "OAuth2 Authorization code was already redeemed" error when getting an access code for an Office 365 resource

We have the following steps to authenticate with Office 365 and get the access tokens required to request calendar events...
Send the user to https://login.microsoftonline.com/common/oauth2/authorize?response_type=code , passing the client id and a redirect URL
The user logs in and is redirected back to the redirect URL with a ?code= query string value
Make an API request to https://login.microsoftonline.com/common/oauth2/token to get a token for the https://api.office.com/discovery/ resource, passing the code that we got in step 2
Store the access token and refresh token which is returned
Make an API request to https://api.office.com/discovery/v2.0/me/services to get a list of end points that we have access to
For the end point that I want to work with (Office 365 Exchange) , make an API request to https://login.microsoftonline.com/common/oauth2/token to get a token for the https://outlook.office365.com/ resource, passing the code that we got in step 2
Store the access token and refresh token which is returned
Use the access token from step 7 to fetch calendar events
This used to work fine, but today I'm getting a different result from the API call on step 6
I used to get an access token and refresh token which I could use for the outlook.office365.com resource, but now I'm getting an error message like the following...
{
"error":"invalid_grant",
"error_description":"AADSTS70002: Error validating credentials. AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token.
Trace ID: 37f11c2c-6450-4040-b297-48268c6d4b00
Correlation ID: afdeb0cb-c14a-4267-9a5d-2422a1f84d62
Timestamp: 2019-01-29 22:26:24Z",
"error_codes":[70002,54005],
"timestamp":"2019-01-29 22:26:24Z",
"trace_id":"37f11c2c-6450-4040-b297-48268c6d4b00",
"correlation_id":"afdeb0cb-c14a-4267-9a5d-2422a1f84d62"
}
So it looks like I need to pass a different code the second time I make a request to get access tokens, but where do I get this different code from?

Do I need to refresh the access token when using Microsoft Graph and How to do it?

I'm using Microsoft Graph SDK for my iOS Application.
Do I need to manually refresh the access token when it expired?
The access token I'm talking about is:
NXOAuth2AccountStore.sharedStore().accounts[0].accessToken
I have tested that I can still query even the accessToken expired. At the time I first logged in, the expired time is 3600 secs. So, I waited 2 hours, test to get user info, events again and still can get it.
I have dump "accessToken.hasExpired" and "accessToken.expiresAt" to make sure access token is expired
Thanks
* More Details *
I follow the sample here:
https://github.com/microsoftgraph/ios-swift-connect-sample
I cannot find any documents about refresh access token on Microsoft Graph:
https://graph.microsoft.io/en-us/code-samples-and-sdks
Yes, you need to refresh tokens periodically when using Graph in your application. More detailed documentation is available through Azure AD's site: https://learn.microsoft.com/en-us/azure/active-directory/active-directory-authentication-scenarios
The suggested auth library you are using contains a method for refreshing this token:
#implementation NXOAuth2AuthenticatorRefreshCallback
If I haven't answered your question, could you be more specific about what you are trying to accomplish? Are you able to use an expired token or are you unable to refresh your old one?
Use this code whenever you need to refresh the access token.
This will act as a patch to predefined code provided in graph sdk and you can extract the token from the method :
+(id)tokenWithResponseBody:(NSString *)theResponseBody tokenType:(NSString *)tokenType;
[MSGraphClient setAuthenticationProvider:AppDel.authentication.authProvider];
_graphClient = [MSGraphClient client];
NSMutableURLRequest * sampleReq = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://graph.microsoft.com/v1.0/me"]];
[_graphClient.authenticationProvider appendAuthenticationHeaders:sampleReq completion:^(NSMutableURLRequest *request, NSError *error){
if(error == nil)
{
}
else
{
[self showToast:#"" message:#"Failure in refresh 0365 token"];
}
}];

ADALiOS seems to be redirecting my login to login.live.com

I'm trying to use ADALiOS framework v1.2.4 to acquire a token with a specific resource and clientId. I'm using a custom authority hosted on 'microsoftonline.com'.
I create an authentication context from within a VC thusly:
ADAuthenticationError *error = nil;
ADAuthenticationContext context = [[ADAuthenticationContext alloc] initWithAuthority:#"https://login.microsoftonline.com/somethingcustom/oauth2/v2.0/authorize?p=some_signin_policy"
validateAuthority:YES
tokenCacheStore:nil
error:&error];
context.parentController = self;
And then I proceed to acquire a token thusly:
[context acquireTokenWithResource:#"someResource"
clientId:#"someClientId"
redirectUri:[NSURL URLWithString:#"someAppScheme://moreStuff"]
completionBlock:^(ADAuthenticationResult *result) {
NSLog(#"Was here!");
}];
What I would expect to happen is that a UIWebView would open and redirect me to my custom authority sign-in page where I can enter my credentials. To confirm, that's what I see when I type in my custom authority URL into any old browser.
However, instead what I see happening is that the code redirects me to login.live.com that (A) looks different and (B) doesn't recognize my login credentials.
When I turn verbose logging on, I see the following line in the logs:
VERBOSE: HTTP Protocol. Additional Information: HTTPProtocol::connection:willSendRequest:. Redirect response: https://login.microsoftonline.com/somethingcustom/oauth2/authorize?response_type=code&client_id=someClientID&resource=someResourceID&redirect_uri=something%3A%2F%2Fsomethingelse&state=bunchOfStuff&x-client-CPU=64&x-client-DM=iPhone&x-client-Ver=1.2.4&x-client-SKU=iOS&x-client-OS=9.2.1&client-request-id=70E51F25-FD3D-4F98-8EDF-04CD19320A98. New request:https://login.live.com/login.srf?wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=estsredirect%3d2%26estsrequest%3drQIIAWVQTWsTUQD0ZVfQXFo8iKUHPUQQ4W33vX2bbAqCxdhtpEnoJhF3b-9ru5u-l63Z3TTJj_BQL6U_wWNPpf9ByFG8CIIg_gIvgokHETzMMMwMc5jqbWRhizw1kIV2a9gWji0wgq5w65Aw2oBNwWLYWAVe3LQxJfXJverm44ffTp_9uui9-2J-3MLVi2sAlgB8B-C8cn_Yfnm0ZzvIUwI9P9Y0VRbP9LICPlc2entlkeA1ZZN0Ib_-55wbGxldO9aaeSbkpfEehAOUiYPgjM-Hi6P-cB6kK-jZNMKqPNToNHReTTluauE3S-arMuq7I4Zt3TmJ8uhNgrgTFEzvz8K-O2X_9l_86Wk-QjpqJarrd87Cvk26g_bscBCchIuw6LX2VWeOkq7fJr1WMIr81yoctT8YtVh4knCbQs9dPUYEr0MPSQdSwjyPcCElw1fGtpKzMi_0mL6lVjbWKZ9kERYX61NujFodIe46jEFkYxsSKuRqJKaQNmKXUSFiD6FPBlia4IdZvQM2Kw_MR7eegJ8muLm7lWdaFkk6Pt7d2fmrpcrlbw2&id=. ErrorCode: 0.
I'm guessing I'm doing something very wrong, but I can't figure out what it is. Any suggestions?
For building iOS apps with Azure AD B2C, I'd recommend starting with this tutorial: https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-ios/. The version of ADAL you are using doesn't support B2C.
For automatically redirecting to custom authority sign-in page instead of common Microsoft landing page, I use extraQueryParameters while getting the token as:
[context acquireTokenWithResource:#"someResource"
clientId:#"someClientId"
redirectUri:[NSURL URLWithString:#"someAppScheme://moreStuff"]
extraQueryParameters:#"domain_hint=AzureADDomain.co.uk"
completionBlock:^(ADAuthenticationResult *result) {
NSLog(#"Was here!");
}];
Hope this helps someone.

Developer Authenticated Identities with Amazon

I'm not familar very well with Amazon so have few questions:
I want to perform login for users on amazon with Cognito using my own custom backEnd solution. Found this like possible solution. This backEnd return after registering and logging for me
{
identityId = "eu-xxxx-x:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx";
login = 1;
token = "eyJraWQiOiJldS13ZXN0LTExIiwidHlwIjoiSldTIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiJldS13ZXN0LTE6NGFmNDdmMWItZDNmOC00NmZkLWI2MzEtZGE2OGU3ZmRmYzE1IiwiYXVkIjoiZXUtd2VzdC0xOjIwNjBiZDc3LWEwNDAtNGI4OC05MDU4LTczMGY3Y2RmNGQyZSIsImFtciI6WyJhdXRoZW50aWNhdGVkIiwibG9naW4ud3A0Lm15YXBwIiwibG9naW4ud3A0Lm15YXBwOmV1LXdlc3QtMToyMDYwYmQ3Ny1hMDQwLTRiODgtOTA1OC03MzBmN2NkZjRkMmU6a2lyaWxsLmdlQGdtYWlsLmNvbSJdLCJpc3MiOiJodHRwczovL2NvZ25pdG8taWRlbnRpdHkuYW1hem9uYXdzLmNvbSIsImV4cCI6MTQ1NTI5MDA5OSwiaWF0IjoxNDU1Mjg5MTk5fQ.FGnBUEQZ3wDENFNa_g29l2UhlIAklBnLdqpMomSE3_ayesNV1dqGyzAMQCvwyr4XdJpB3lI0KF-k3wc4t8BKPYg5QKrZ-q-aNkjwp34tHFMIr8vGw4vbZiKB6XnGMRghYSbPtuwwFG80ibZMAAXik4nld8sGxoQSrCjTubPKU4I9Mzi6lJQsDGAZxmm56E2lVSeBw2nZbE1iwRDhJf6hHJsKOLceDDtWoknRX3NHeNuoueNLS1JrbphD8wVqejxhEjrK-qucoUL_uj81GxYUkyONQtu-3B79epsXIsxvU_zW1MwVufFg5p5ID83F1Cic77QkzF2FJnEJIadEG6R_yw";
}
I want follow this guide. But according to this i should provide IdentityProviderName and token and set it like after any social's login :
credentialsProvider.logins = #{
#(AWSCognitoLoginProviderKeyFacebook): token
};
As IdentityProviderName i used name that was setted in config.json
"DEVELOPER_PROVIDER_NAME": "<ProviderName>"
But i got exception that i havent any provider
AWSCognitoCredentialsProvider
getCredentialsWithCognito:authenticated:]_block_invoke |
GetCredentialsForIdentity failed. Error is [Error
Domain=com.amazonaws.AWSCognitoIdentityErrorDomain Code=8 "(null)"
UserInfo={__type=InvalidParameterException, message=Please provide a
valid public provider}]
When i go to IAM Console to create providerID it ask me to choose type of provider (OpenID or SAML)
So i'm really dont understand what exactly should i put there? what type to select.
For server i used Lambda service with DynamoDB service, for web pages used S3.
According to this flow:
I already complete first 4 steps and i'm stack on 5 step, So i need only to send this data to Cognito (no needs to prepare my custom dev provider, because i already have idendityID and token, thats looks like incorrect).
As result
developer Auth - only for login method from lambda but when i try to use DynamoDB user are not auth.
All roles are added - currently i add temp role for Unath users
{
"Sid": "DynamoDBPolicy",
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"*"
]
}
All works (huh) but it's not really good
Question - how to make Cognito DevAuth with received token from my backEnd solution placed on Lambda? Does i miss something?
UPDATE
thanks to #Rachit Dhall
AWSCognitoIdentity *cognitoIdentity = [AWSCognitoIdentity defaultCognitoIdentity];
AWSCognitoIdentityGetCredentialsForIdentityInput *input = [[AWSCognitoIdentityGetCredentialsForIdentityInput alloc] init];
input.identityId = [task.result valueForKey:#"identityId"];
input.logins = #{
#"cognito-identity.amazonaws.com" : [task.result valueForKey:#"token"]
};
[cognitoIdentity getCredentialsForIdentity:input completionHandler:^(AWSCognitoIdentityGetCredentialsForIdentityResponse * _Nullable response, NSError * _Nullable error) {
//how to update my configuration for AWS with
//AWSCognitoIdentityCredentials object?
}];
but not sure what to do with AWSCognitoIdentityCredentials received in response - how to update my configuration?
Also found next:
Does it mean that there is no another way for this? Or something missed?
The error which says present a valid public provider issue, is because you are calling GetCredentialsForIdentity with your login provider. But instead the flow is that you call GetOpenIdTokenForDeveloperIdentity from your backend, you get a token as response. Then to get credentials you call GetCredentialsForIdentity with key cognito-identity.amazonaws.com in the logins parameter and value as the token received from previous call.
Update:
To make sure Amazon DynamoDB uses credentials vended by Amazon Cognito you can create a default AWSServiceConfiguration with your credentials provider and use this to initialize the client.
// create a configuration that uses the provider
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 provider:credentialsProvider];
// get a client with the default service configuration
AWSDynamoDB *dynamoDB = [AWSDynamoDB defaultDynamoDB];

Bigcommerce - How to request Authorization Code/Access Token

In my application, the user when installs the app, needs to fill a registration form. I need to save the access_token along with the user instance.
So, if the user is unregistered, I redirect to the signup form ie. I dont save the access_token, but at this time, the app is registered. Which means, suppose when the store admin logs back in to the app, he does not get the auth code again, but gets signed_payload.
Since, I dont want to store, unregistered users on my database, I prefer calling a api, that would grant me auth code and/or access_token.
Is there any such call I can make?
To answer your question, the access token can only be obtained at the point of the initial app install, when the user installs the app for the very first time. This is the only time that BigCommerce will send the information required to obtain the access token.
Therefore your app should always save the access_token at the point of install. Your registration page should be prompted after obtaining and saving the access token. If for some reason the user installs the app and does not complete the registration, then you should simply just check on your end if the registration was finished or not, and if it wasn't then you should display it during the app load phase as a requirement before displaying your main app dashboard.
Since you didn't specify a programming language, I'm going to illustrate one in Python.
There are two parts you mentioned, registration/access token and signed payload.
The initial callback flow would look something like this:
#app.route('/bigcommerce/callback')
def auth_callback():
# Put together params for token request
code = flask.request.args['code']
context = flask.request.args['context']
scope = flask.request.args['scope']
store_hash = context.split('/')[1]
redirect = app.config['APP_URL'] + flask.url_for('auth_callback')
# Fetch a permanent oauth token. This will throw an exception on error,
# which will get caught by our error handler above.
client = BigcommerceApi(client_id=client_id(), store_hash=store_hash)
token = client.oauth_fetch_token(client_secret(), code, context, scope, redirect)
bc_user_id = token['user']['id']
email = token['user']['email']
access_token = token['access_token']
The flow using a signed payload would look something like:
#app.route('/bigcommerce/load')
def load():
# Decode and verify payload
payload = flask.request.args['signed_payload']
user_data = BigcommerceApi.oauth_verify_payload(payload, client_secret())
if user_data is False:
return "Payload verification failed!", 401
bc_user_id = user_data['user']['id']
email = user_data['user']['email']
store_hash = user_data['store_hash']
When initially creating a user in your database, you can also denote the sign up date through a function of your code and then do a periodic cron job to check if they have a registered account with you. There's not an endpoint where we store whether they completed registration with you since that is a function of your app.

Resources