Android, AccountManager and OAuth - java

I'm sure this is basic and I'm missing something. I've read through other answers on SO, I've googled, I've read resources and I just can't wrap my head around what I need to do.
I'm trying to figure out how to write an app that connects to Twitch's API, specifically how to authenticate with Twitch's api. Their documentation is here: https://github.com/justintv/Twitch-API/blob/master/authentication.md
I've created an app and stored my keys.
Now comes the part where I want my user to click a button which launches the authentication on their website. From what I can tell I do this by using an AccountManager. Except... I can't figure out what I'm supposed to do.
Here's the excerpt I've found online:
AccountManager am = AccountManager.get(this);
Bundle options = new Bundle();
am.getAuthToken(
myAccount_, // Account retrieved using getAccountsByType()
"Manage your tasks", // Auth scope
options, // Authenticator-specific options
this, // Your activity
new OnTokenAcquired(), // Callback called when a token is successfully acquired
new Handler(new OnError())); // Callback called if an error occurs
According to twitch's documentation I want to send the user to:
https://api.twitch.tv/kraken/oauth2/authorize
?response_type=code
&client_id=[your client ID]
&redirect_uri=[your registered redirect URI]
&scope=[space separated list of scopes]
&state=[your provided unique token]
And I simply have no idea how these two things need to be combined.

Firstly, I recommend to read the OAuth2 RFC. This should cover everything you need to know.
The AccountManager code snippet won't help you much unless there already is an app that provides authentication for Twitch. If that's not the case you either need to use an existing OAuth2 library or implement your own.
You could write your own AccountAuthenticator but that's a different challenge (and you still need some kind of OAuth2 client).
Doing it yourself is not that hard, see below.
Steps to implement it yourself
Twitch recommends to use the "Implicit Grant Flow" for mobile apps. That's what I'm going to describe below.
1. Get a client ID
Register your app as outlined in Developer Setup to get a client ID
As redirect URI you can use something like https://localhost:12398/, the actual port doesn't really matter.
2. Build the authentication URL
In your client app you need to construct the authentication URL like so:
https://api.twitch.tv/kraken/oauth2/authorize?
response_type=token&
client_id=[your client ID]&
redirect_uri=[your registered redirect URI]&
scope=[space separated list of scopes]
Apparently [your client ID] should be replaced by the client ID you've received from Twitch, same goes for [your registered redirect URI] (that's the URL above, i.e. https://localhost:12398/). [space separated list of scopes] is the list of scopes (i.e. features your want to access), see Scopes. Make sure you URL-encode the parameter values properly.
Assuming your client ID is 123456 and the scopes you need are user_read and channel_read your URL would look like this:
https://api.twitch.tv/kraken/oauth2/authorize?
response_type=token&
client_id=123456&
redirect_uri=https%3A%2F%2Flocalhost%3A12398%2F&
scope=user_read%20channel_read
Note that you should also pass a state parameter, just use a randomly generated value. You can also append the (non-standard) force_verify parameter to make sure the user actually needs to log in each time (instead of continuing a previous session), but I think you can achieve the same by clearing the cookie store (given that you open the URL in a webview in the context of your app) before you open the login page.
With a random state the URL would look like this:
https://api.twitch.tv/kraken/oauth2/authorize?
response_type=token&
client_id=123456&
redirect_uri=https%3A%2F%2Flocalhost%3A12398%2F&
scope=user_read%20channel_read&
state=82hdknaizuVBfd9847guHUIhndzhuehnb
Again, make sure the state value is properly URL encoded.
3. Open the authentication URL
Ideally you just open the URL in a WebView inside of your app. In that case you need to intercept all request to load a new URL using WebViewClient.shouldOverrideUrlLoading
Once the client is redirected to your redirect URL you can close the webview and continue with step 4.
Theoretically it's possible to utilize the default browser to do the authentication, but I would have security concerns since an external app could learn about your client ID and the access token.
4. Extract the access token
The actual URL you get redirected to in step #3 will have the form:
https://[your registered redirect URI]/#access_token=[an access token]&scope=[authorized scopes]
or to pick up the example
https://localhost:12398/#access_token=xxx&scope=user_read%20channel_read
Where xxx is the actual access token.
If you passed a state it will be present like so:
https://localhost:12398/#access_token=xxx&scope=user_read%20channel_read&state=82hdknaizuVBfd9847guHUIhndzhuehnb
All you have to do now is to parse the (URL encoded) access token, scope and state. Compare the scopes and state to the ones that you actually sent. If they match you can start using the access_token to authenticate.
Note According to the OAuth2 RFC, the response URL MUST also contain a token_type and it SHOULD contain an expires_in duration in seconds.
Once you received the access token you can use it to authenticate as described here.
Access tokens issued by the Implicit Grant Flow usually expire after a certain time and the user needs to authenticate again. The Twitch documentation doesn't mention any expiration time, so it's possible that the token is valid forever. So make sure your app doesn't store it or store it in a secure way (like using Android's key store provider to generate and store a key to encrypt the access token).
If the implicitly issued access token expires you could consider using the "Authorization Code Flow". That's quite similar but it contains an additional step to receive the access token and a "refresh token" that can be used to renew the access token. I leave it up to you to figure out how that works.

Related

How to retrieve access token from Reddit via OAuth flow with Java

I have my authorisation url - https://www.reddit.com/api/v1/authorize?client_id=xuJKekGTr1-V8Q&response_type=code&state=dfDfsd4gdf&redirect_uri=http://localhost:8080/redditimageuploader/callback&duration=permanent&scope=submit
But I don't really know what to do from here? I've found a few guides online but it's just a lot of jargon I don't really understand. When I click on the "allow" button, it takes me to the url that I defined as my redirect_uri, and appended to the end of the string is the state that I set, as well as code= and then a string - so I assume I need to do something with those, but I don't know what.
I was wondering if there is a super simple "explain like I'm 5" step-by-step guide on what to do from here?
It's a standard OAuth flow. From the doc :
When the user clicks the "Sign on with Reddit" button on your website, you must redirect the user to the authorisation URL at Reddit - the one in your question, starting with https://www.reddit.com/api/v1/authorize and enriched with the request params you specified. Reddit will then ask the user to sign in, and whether or not he wants to authorise your app access to the requested scope. See https://github.com/reddit-archive/reddit/wiki/OAuth2#allowing-the-user-to-authorize-your-application
If the user agrees, then Reddit will redirect the user to the redirect URI you specified as request param in the authorisation URL (in your case, http://localhost:8080/redditimageuploader/callback). Reddit will add a state request param: you need to ensure that this is the same as the one in your request.
Retrieve the access token with a POST request to https://www.reddit.com/api/v1/access_token, including the following data in your data: grant_type=authorization_code&code=CODE&redirect_uri=URI. Replace CODE with the value you received and URI with your same redirect URI as in the first step.
The response to this third step should return you an access token: store this for future requests on behalf of the user. See https://github.com/reddit-archive/reddit/wiki/OAuth2#retrieving-the-access-token
Extra steps are available and documented for error handling and access token operations (invalidation / renewal).
So, once you've correctly implemented the first step, all you need to do is create an endpoint (the one called when your redirect URI is redirected to) which will :
check the state request param
Retrieve the access token (third step) and store it
Let me know if this is clear enough.

Get access token using Spring Security with a specific use-case

Is this use-case supported for Spring Security 5, or something else, where we don't have to reinvent the wheel? Thoughts on how to (re)implement this better?
Details are as follows. 3rd party vendor supplied endpoints. We pull info from upstream source then forward to the downstream vendor. Only 2 APIs are required:
Request Access Token
Save Info
Both are actually being called via a gateway. We've been given specifics:
(A)
The token request requires Basic Auth (standard header - usual base64 encoded). Gateway User and Gateway Password are provided.
Credentials for request token are provided to us:
Grant Type = password
Consumer Id
Consumer Secret
Account User
Account Password
It responds with an access token and few other details we don't really care about and of zero value to our use-case.
There is no expires_in info in the response. But I've tested it multiple times to know it does expire. Not sure how long right now, I could do more tests to determine that.
(B)
The save request requires a different custom header for the same Gateway User / Password, then a Bearer Authorization header in the call to the Save Info API.
Right now, all implementations for above are using RestTemplate. Working fine already. But a token is requested for each save which is costly. Before writing any caching, or some other logic to wait XY minutes before another token request is made, I would appreciate any other options which may already be possibly handled via Spring-specific libraries or further advise on how to handle this scenario.
Apologies if this is not the right place to ask this, or it has already been asked before. Been searching for a similar use-case but can't seem to find one.
Thanks.
Try any one of the option
You can use OAuth2ClientContext which stores your access token.
final OAuth2RestTemplate restTemplate=new OAuth2RestTemplate(resourceDetails, clientContext);
You can create session & store your token & user details inside it.
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user, null,null);
SecurityContextHolder.getContext().setAuthentication(authToken);
from option 1 Or option 2 you can then fetch existing token for each request at your Filter e.g. PRE_AUTH_FILTER
Then check if token expired - if yes request new token Or call refresh token
Check Oauth2 expires_in in below :-
https://www.rfc-editor.org/rfc/rfc6749?

Modify audit trail in Alfresco using a custom webscript

I am writing a custom Java webscript that accepts document noderef and an external username (string value) as parameters. I have auditing enabled and the audit log shows access to the document when I call the webscript. Now I wanted to know if it is possible to modify the audit trail so that when it shows the log for that particular document it also shows the name of the external user.
webscript url: http://localhost:8080/alfresco/service/node/{noderef}/user/{user}
On calling this I get the following output in log:
Extracted audit data:
Application: AuditApplication[ name=alfresco-access, id=1, disabledPathsId=2]
Values:
/alfresco-access/transaction/sub-actions=readContent
/alfresco-access/transaction/action=READ
/alfresco-access/transaction/node=workspace://SpacesStore/c21db432-4ad6-4af2-8bcf-78bc89724afe
/alfresco-access/transaction/type=cm:content
/alfresco-access/transaction/path=/app:company_home/app:shared/cm:audit-services-context.xml
/alfresco-access/transaction/user=admin
New Data:
/alfresco-access/transaction/sub-actions=readContent
/alfresco-access/transaction/action=READ
/alfresco-access/transaction/type=cm:content
/alfresco-access/transaction/user=admin
/alfresco-access/transaction/path=/app:company_home/app:shared/cm:audit-services-context.xml
I want to store the {user} also in the audit trail.
You can try to use AuthenticationUtil.setFullyAuthenticatedUser. I think this should help you. But I didn't test this.
You probably do not want to do that, at least not in the way you describe, not without making extra security precautions.
This goes IMHO opinion against security standards, if admin needs to read a document,the operation needs to be logged with his username, if a normal user needs to access a document, he needs to be properly authenticated for that operation.
Judging from the little context I have I would say this is actually an integration with some other app that does not share SSO with Alfresco. So I would recommend a solution of the following :
Use proper SSO between Alfresco and your application, have the concerned user ping the right endpoint in Alfresco and let SSO authenticate the request properly for you.
Use a shared secret (something like a shared passphrase to encode encode the authority name in the request + proper authentication subsystem or request filter to handle that) or a key pair (something like securecomms between solr and alfresco) to be able to securely pass on authority information to the request
Use a system account (preferably not admin, but one that is dedicated to this usecase/application integration) to generate a valid alf_ticket for the user in question, and have your app attach that ticket to the request. (Of course, your "impersonate" webscript would need to check for the right system/integration username, before running the snippet to get the alf_ticket from a runAsSystem block). In this case, I would also recommend not using the admin account for this but rather use a user with no permissions at all except for this usecase.
If you are going to opt for the quick implementation that you have, I would recommend at least the following :
You need to make sure that not any user can ping that webscript and that only admin/system user can actually access that webscript.
You probably should log the whole impersonation operation in the audit trail (either using the same audit entry or a separate one), so that it would be clear that this is actually an operation that was made on behalf of the user and not directly by the user himself
If you use the webscript in question for anything other than reading the content of the node (Can be the case also if you have a onReadContent behaviour that has some nasty AuthenticationUtil.setFullyAuthenticatedUser as well), and you require that operation to be logged as system/originally authenticated user, You will probably have a hard time doing that... and you should switch to a more robust approach!

OAuth protocol / java- Scribe : storing the token?

I've been playing with the [scribe API][1] and a basic example e.g:
https://github.com/fernandezpablo85/scribe-java/blob/master/src/test/java/org/scribe/examples/TwitterExample.java
In a command line oriented interface, the user is asked to open a web-browser and to copy'n paste the "accessToken".
Once the user has copied the "accessToken", I want to avoid this "browser step" in the later invocations of the tool: can I store the "accessToken" somewhere to re-use it later ? would it work for any server (Twitter ? Flickr... ) ? How should I change the code to reuse the previously saved "accessToken" ?
Thanks,
In the case of the Twitter API you should store the access token as it represents the user's permission for your application to access their account.
However, bear in mind that the token may be revoked by the user, so ensure your application is able to obtain it again.
To change the code to use a previously saved accessToken all you would have to do is look up the token for the current user - perhaps it's retrieved from a database, and then start making requests. Essentially you would just skip the whole "obtaining request token" block of code.

Register with facebook - Java server side authentication - Getting access token

To get user details, facebook docs suggests making this call:
https://graph.facebook.com/me?access_token=YOUR_USER_ACCESS_TOKEN
When a user signs up using facebook's "Register plugin", how do I get this user access_token from the signed request. The latter contains a oauth_token, but I couldn't find documentation on how to obtain the access_token. I use java server side authentication technique (I don't use the javascript way..)
Specifically, the docs say:
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID
&redirect_uri=YOUR_REDIRECT_URI
&client_secret=YOUR_APP_SECRET
&code=CODE_GENERATED_BY_FACEBOOK
This answer recommends passing a blank redirect uri. But where does the "CODE_GENERATED_BY_FACEBOOK" come from in the REGISTRATION flow? I tried passing the oauth_token from the signed_request as the "code generated by facebook". But it fails verification.
Pointers will be be helpful. Thanks.
Note: I already have the "sign in with facebook" working & I know how to obtain the access_token in that flow. But I would expect a more straight forward way of doing that when the user registers using facebook.
You may have solved your problem, but anyway I place my case;
You must set exactly the same value for the "redirect_uri" parameters for both dialog/oauth and oauth/access_token pages (and unfortunately, this is not stated clearly on Facebook documentation). In my case the URIs in question were:
https:
//www.facebook.com/dialog/oauth?client_id=23902620775&redirect_uri=http%3a%2f%2fwww.mydomain.com%2fMembership%2fLoginCheckFacebook&scope=user_birthday,email
https
://graph.facebook.com/oauth/access_token?client_id=23902620775&redirect_uri=http%3a%2f%2flocal.www.mydomain.com%2fMembership%2fLoginCheckFacebook&client_secret=80a34471e31e7afa0a7a7cd3cfc7&code=AQDsrevxH_Q3-NVts8VBThXCC0z6cCIhdnTMVCE9McsjJYLLy6ZKnlibmi8tWPcP...

Categories

Resources