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

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.

Related

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"];
}
}];

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];

Unauthenticated user to authenticated user on AWS Cognito

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.

Office 365 AD Authentication with ADAL: missing parameter 'client_secret or client_assertion'

I'm trying to authenticate in Microsoft Office 365 Azure AD. For the purpose I'm using Microsoft ADAL library for Objective-C version 1.0. Here is a sample code:
ADAuthenticationError *error;
ADAuthenticationContext *authContext = [ADAuthenticationContext authenticationContextWithAuthority:#"https://login.microsoftonline.com/...." error:&error];
[authContext acquireTokenWithResource:#"https://login.microsoftonline.com/..."
clientId: #"AAAAAA-AAAA-AAAAA-AAAA-AAAAA";
redirectUri:[NSURL URLWithString:#"https://localhost:11111"];
userId:nil
extraQueryParameters:#"client_secret=AAAAABBBBBCCCCC"
completionBlock:^(ADAuthenticationResult *result) {
if (AD_SUCCEEDED != result.status){
NSLog(#"%#", result.error.errorDetails);
} else {
NSLog(#"%#", result.accessToken);
}
}];
I get
Error -- AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'.
ADAL for ObjC only works for public clients, which do not use secrets - and in any case, adding it via extraqueryparameters wouldn't help. How did you register your application in Azure AD? I suspect you might have registered it as a web app (which is a confidential client, requiring client creds when requesting tokens) while it needs to be registered as a native client.

Twitter oauth/access_token 401 Error

I've been working on an all that uses the full OAuth app flow, and I have been running into an issue where I only get back a 401 = "Invalid or expired token" error. I've checked my request with the documentation, and everything looks correct, and I'm stumped. Below is the details of my request.
URL
https://api.twitter.com/1.1/oauth/access_token
HTTP Method
POST
Headers
Content-Type: application/x-www-form-urlencoded
Authorization: OAuth oauth_consumer_key="CONSUMER_API_KEY", oauth_nonce="B4D43B0C-A348-4EB6-9C0B-8B0F4FE8", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1397724299", oauth_version="1.0", oauth_token="TOKEN_FROM_REQUEST_TOKEN_API", oauth_signature="ulYIzTacwC%2FeGUdoCPYsrFEqg4A%3D"
HTTP Body
oauth_verifier=OAUTH_VERIFIER_FROM_REQUEST_TOKEN_API
I have no issues getting the oauth/request_token API to work, and I have appropriately set the permissions in Twitter's app settings. Does anyone have any idea what's going on with this?
Thanks for any help you all may be able to offer.
-- UPDATE --
Another thing to note is that I'm using the STTwitter library to make the requests. It did not have a built-in method to handle the oauth/authorize or oath/authenticate API methods so I'm using the code below.
// get request token, and present login screen
STTwitterAPI *twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey:twitterApiKey consumerSecret:twitterApiSecret];
[twitter postTokenRequest:^(NSURL *url, NSString *oauthToken)
{
authWebViewController = [[TWAuthWebViewController alloc] init];
authWebViewController.url = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.twitter.com/oauth/authorize?oauth_token=%#", oauthToken]];
authWebViewController.completion = ^(TWAuthWebViewController *authViewController, NSURL *oauthCallbackURL)
{
// get the request token and verifier from the URL
NSString *oauthToken = [TWLoginBusiness getOAuthTokenFromURL:oauthCallbackURL];
NSString *oauthVerifier = [TWLoginBusiness getOAuthVerifierFromURL:oauthCallbackURL];
// get user data with the oauth token
[twitter postResource:#"oauth/access_token"
baseURLString:#"https://api.twitter.com/1.1"
parameters:#{#"oauth_verifier" : oauthVerifier}
uploadProgressBlock:nil
downloadProgressBlock:nil
successBlock:^(NSDictionary *rateLimits, id response)
{
NSLog(#"Reponse: %#", response);
completion(nil, nil);
} errorBlock:^(NSError *error)
{
NSLog(#"Error: %#", error);
completion(nil, error);
}];
};
presentAuthController(authWebViewController);
} oauthCallback:twitterApiCallback errorBlock:^(NSError *error)
{
completion(nil, error);
}];
One last note. The part that actually displays the web view controller is not listed here. I wanted to keep the code snippet here focused on the actual API methods, and not the UI logic. Just be assured that the line right after authWebViewController.url.... displays the web view, and then the completion block is called after the user completes the authentication on the Twitter web page. Also the two methods getOauthTokenFromURL and getOauthVerifierFromUrl do in fact return the correct token and verifier. The STTwitter library actually saves out the token it's self, so that is why it's not manually passed into the logic below. The logic generates the request above.
Thanks
The full OAuth flow is already implemented in STTwitter library.
Check out iOS demo for a working example of web-based authentication through Safari.
What you are trying to do here is a web-based authentication inside the application.
Although it is totally feasible, I did not include this workflow in the demo project because it is considered a bad practice.
Indeed, the point of OAuth is that the user does not want to enter her Twitter credentials in your own application, but asks Twitter to send your app dedicated OAuth tokens instead.
Let me know if you need more help with STTwitter.

Resources