RestKit - Repeat request if auth timeout - objective-c

I'm talking to a .NET Web API 2 service, using it's default "Individual User Accounts" authentication. I'm using RestKit 0.22.0.
My issue is that when a session timeout occurs, I want to have the iOS app re-authenticate with the stored credentials and then replay the request. The only way I've found to do this is like this - asked a year ago. However I do not think that the answer should be to check if the status code is 401, re-authenticate, retry the request in every failure block. As that's just a lot of copy and pasting!
Is there a better way to accomplish what I want?

Call a common method from the failure block which takes the required inputs. Preferably you might want to have multiple common methods which are capable of verifying the response details and restarting the process automatically (so 1 for GET, 1 for POST, ...).
Pseudo code
- (void)do something with this info:
{
... calculate method specifics then call general method ...
[self getObjectsAtPath:urlArg parameters:p success:s];
}
- (void)getObjectsAtPath:urlArg
parameters:p
success:s
{
[RK getObjectsAtPath:urlArg
parameters:nil
success:
{ -- Yay -- call success callback
s();
}
failure:
{ -- Humph -- retry
... do auth updates, then retry ...
[self getObjectsAtPath:urlArg parameters:p success:s];
}
}

Related

Custom error status code with gqlgen + go gin

Recently I have been updating my GO REST APIs into graphQl API's and I came across issue where I am unable to customise my status code with gqlgen.
Response I got
Headers
Status Code: 200 OK
{
data: null,
errors: [
{message: "Unauthorized access", path: ["..."]}
]
}
Expected Header
Status Code: 401 UNAUTHORISED
Any help would be really appreciating!
Assume you have a gqlgen resolver similar to this:
func (r *queryResolver) SecretItems(ctx context.Context, userID string,
password string) ([]SecretItems, error) {
// ...
if !isAuthorized(userID, password) {
return nil, errors.New("Unauthorized access")
}
// ...
}
then the described behavior is expected. Errors should be returned as part of
the response body.
GraphQL is transport agnostic. While it is often served over HTTP, it might be
served over other client-server Protocols as well. Handling errors in the
response body requires no assumptions about the protocol. Hence, you shouldn't
rely on HTTP status codes.
Handling errors in the response body has another advantage: Assume a request
contains multiple queries. Some of them succeed, some of them fail. Then the
response can contain the result of successful queries under data and errors
related to failed queries under errors.
References:
GraphQL website
Specification: Response
Hasura: GraphQL vs REST
Possible reason why you expected a 401 status code
The gqlgen docs on
authentication contain an example
where 401 status code is returned.
Why? This happens in a http handler used as middleware on the chi http server.
The 401 status code is not returned by a GraphQL resolver.

Difference between EnsureSuccessStatusCode and Assert.Equal(HttpStatusCode.OK, response.StatusCode) in XUnit ASP.Net Core WebAPI Tests

I read in a Book (ISBN 1838550313, 9781838550318 S.315) that they check a WEB-API Request with both EnsureSuccessStatusCode() and Assert.Equal(HttpStatusCode.OK, ..) in one validation Method. But is the second Assert call not unnecessary to check if the HTTP-Status is 200 or not? What is the difference and what is the best practice?
HttpResponseMessage.EnsureSuccessStatusCode is implemented like this:
public HttpResponseMessage EnsureSuccessStatusCode()
{
if (!IsSuccessStatusCode)
{
throw new HttpRequestException(…, inner: null, _statusCode);
}
return this;
}
So it just checks the value of IsSuccessStatusCode which is implemented like this:
public bool IsSuccessStatusCode
{
get { return ((int)_statusCode >= 200) && ((int)_statusCode <= 299); }
}
So a status code is considered to be successful, if it is in the range [200, 299]. This matches the definition of the HTTP status codes.
If the status code value is not a successful code, then the EnsureSuccessStatusCode method will throw an exception. So it is a quick way to stop the execution in cases where the request failed.
Asserting for equality with HttpStatusCode.OK checks if the status code is exactly 200. This also means that other successful codes would be rejected. Whether that is an appropriate thing to do depends on the API you are testing. Many RESTful APIs will often return different successful status codes depending on what happened. For example, an API might return “201 Created” to express that a resource has been created.
If the test wants to explicitly ensure that the response has a status code “200 OK”, then calling EnsureSuccessStatusCode is not needed. Otherwise, if you want to accept any successful status code, just calling EnsureSuccessStatusCode will be enough for the test since a thrown exception will usually fail the test.

API of Polarion ALM occasionally does not authorize any request

I have wrote some Python code that logs in and reads some data from Polarion ALM server via API (more informarion about Polarion API: https://almdemo.polarion.com/polarion/sdk/index.html). In my code I have used zeep Python package to handle SOAP.
My algorithm is simple:
1) Log in via logIn web service (https://almdemo.polarion.com/polarion/sdk/doc/javadoc/com/polarion/alm/ws/client/session/SessionWebService.html#logIn-java.lang.String-java.lang.String-)
2) Add current session to header - so the current session remain alive.
3) Try to read some data, for example via getRootProjectGroup web service (https://almdemo.polarion.com/polarion/sdk/doc/javadoc/com/polarion/alm/ws/client/projects/ProjectWebService.html#getRootProjectGroup--).
4) Regardless of what is happening I close the current session via endSession web service (https://almdemo.polarion.com/polarion/sdk/doc/javadoc/com/polarion/alm/ws/client/session/SessionWebService.html#endSession--).
What I observed:
Ocassionally, at point 3 I receive response with Authorization Error (snippet with response):
<soapenv:Fault>\n <faultcode>soapenv:Server.generalException</faultcode>\n <faultstring>Not authorized.</faultstring>\n <detail>\n <ns1:stackTrace xmlns:ns1="http://xml.apache.org/axis/">Not authorized.\n\tat com.polarion.alm.ws.providers.DoAsUserWrapper.invoke(DoAsUserWrapper.java:37)\n\tat org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)\n\t..
or everything is good and I receive:
{
'groupURIs': {
'SubterraURI': [
'subterra:data-service:objects:/default/${ProjectGroup}Group'
]
},
'location': None,
'name': 'ROOT_CTX_NAME',
'parentURI': None,
'projectIDs': None,
'uri': 'subterra:data-service:objects:${ProjectGroup}Group',
'unresolvable': False
}
What surprises me the most:
- I always uses the same credential (username and password)
- the session ID of the in request (point 3) is the same as in the server response during log in (point 1) so the session shall remain alive
- if I put my code in the loop (for example 1000 executions), the result for all attempts is always the same (1000 successes or 1000 failures), even if I add a wait (e.g. 1s) between the attemps
I would like to know why server rejects some of the requests. Is it some kind of Polarion server issue? How could I make a work around to somehow connect with the server and be able to read some data from the server even if it reject my first request.
It appears that it is issue with SOAP client (and relatively popular one). To fix it, I have turned off TLS verification. More details in:
https://python-zeep.readthedocs.io/en/master/transport.html

How to activate OneLogin Protect push challenge with SAML assertions

I have figured out that to use OneLogin Protect with saml assertions, you need to call the SAML verify factor without the OTP provided, and read about a similar question here, but if I simply put this in a loop with a sleep, the User is bombarded with OTP calls, until they hit "Accept". If the user takes 10-20 seconds to pull their phone out, and I sleep for 20 seconds, that means that users who have their phone immediatly available and could response in 2-3 seconds, have to wait 20 seconds to cover the slower calls.
To adress this, I would like to check the saml assertion verify without sending a push notification, and just the state_token.
I have also noticed that if I were not to use SAML assertions, I could do this as described here: https://developers.onelogin.com/api-docs/1/multi-factor-authentication/overview but that requires me to give either Manage users or Manage All permissions to the API keys. As this is a Python cli utility to handle aws saml auth, and one way or another, we need to distribute those API keys to users, this seems like excessive privileges for a user to log into AWS.
Does anyone know how can I check the status of an OTP for OneLogin Protect with SAMl assertions, without creating a new OTP call every time I check the status?
Example code
ol_client = OneLoginClient(
'client_id',
'client_secret',
'us',
)
saml_resp = ol_client.get_saml_assertion(
'ol_username',
'ol_password',
'aws_app_id',
'subdomain'
)
call_result =ol_client.get_saml_assertion_verifying(
'onelogin_aws_app_id',
device.id,
saml_resp.mfa.state_token
)
if call_result is None:
verify_result = None
while verify_result is None:
sleep(1)
verify_result =ol_client.get_saml_assertion_verifying(
'aws_app_id',
device.id,
saml_resp.mfa.state_token
)
I've found an answer by now. Just forgot to update:
The API exposes a Message field in the verifyFactor endpoint. While it's pending for the user to act on it, it will contain the string pending.
rMfa, err = c.VerifyFactor(token, &pMfa)
for strings.Contains(rMfa.Message, "pending") && timeout > 0 {
time.Sleep(time.Duration(MFAInterval) * time.Second)
rMfa, err = c.VerifyFactor(token, &pMfa)
if err != nil {
s.Stop()
return nil, err
}
timeout -= MFAInterval
}
I resorted to check every second until the timeout expired. The full implementation can be seen here: https://github.com/allcloud-io/clisso/blob/master/onelogin/get.go#L133

How to tell whether Accounts.addEmail succeeded or failed, and if it failed, the reason why

I have a page where the user can type in a new email address and then this method attempts to add it to their account:
Meteor.methods({
add_new_email: function(address)
{
Accounts.addEmail(Meteor.userId(), address);
}
});
I'm using the accounts-password package in Meteor.
I'd like to give the user meaningful feedback after they try to add the new address, in particular if it failed why did it fail? I have looked at the docs but there doesn't seem to be any method to find out failure reason.
I know that I can count the user's email addresses before and after trying to add the new one, but that doesn't tell me if the address already belongs to another user, or if it's an existing address of the user's, or whatever is the failure reason.
Is there any way to find out the result of an API call like this?
You can read the information about what this method does here:
https://github.com/meteor/meteor/blob/master/packages/accounts-password/password_server.js#L847
As you can see, the method will fail only in one case:
The operation will fail if there is a different user with an email
only differing in case
Therefore if the method fails you can tell to the user that the email is already registered.
After experimenting some more, it seems that all I need to do is add a callback to my client when I call the method, and check there for an error. Any error is automatically returned to the callback.
Server:
Meteor.methods({
add_new_email: function(address)
{
Accounts.addEmail(Meteor.userId(), address);
}
});
Client:
Meteor.call('add_new_email', 'me#example.com', function(error){
if (error) console.log("got an error " + error.reason);
});
I had not realised that the error from the API would be passed up into my method. Meteor - it's always more clever than I expect!
Note also that you can use Meteor.Error in your methods to throw errors which will be passed up to client callbacks in exactly the same way, see the docs:
if (!Meteor.userId()) {
throw new Meteor.Error("not-authorized", "You must be signed in to write a new post");
}
I know I'm a bit late to the party but I ran into this problem today and found your post.
I needed to be able to tell on the server side whether it failed or not so what I did was put it in a try-catch like so:
let addSucceeded = false;
try{
Accounts.addEmail(user._id, newEmailAddress);
addSucceeded = true;
} catch(err) {}
console.log(addSucceeded);
Only if the Accounts.addEmail does not fail will addSucceeded be set to true. To make sure I don't run into the "fail because it replaced the same user's email address in a different case" scenario, I always toLowerCase() the email address when saving.