I am using Postman to test OAuth 2 from a vanilla AEM install.
Postman can successfully obtain the authorization code from /oauth/authorize after I grant access:
But when it tries to use the code to obtain a token from /oauth/token it receives the following response:
HTTP ERROR: 403 Problem accessing /oauth/token. Reason: Forbidden
Powered by Jetty://
Looking in Fiddler it is doing a POST to /oauth/token with the following Name/Values in the body:
client_id: Client ID from /libs/granite/oauth/content/client.html
client_secret:
Client Secret from /libs/granite/oauth/content/client.html
redirect_uri: https://www.getpostman.com/oauth2/callback
grant_type: authorization_code
code: Code returned from previous request to oauth/authorize
Am I missing something?
Would help if you can list some code snippets on how you are building the url and fetching the token.
Here's an example of how we've implemented very similar to what you are trying to do, maybe it'll help.
Define a service like below (snippet) and define the values (host, url, etc) in OSGI (or you can also hard code them for testing purposes)
#Service(value = OauthAuthentication.class)
#Component(immediate = true, label = "My Oauth Authentication", description = "My Oauth Authentication", policy = ConfigurationPolicy.REQUIRE, metatype = true)
#Properties({
#Property(name = Constants.SERVICE_VENDOR, value = "ABC"),
#Property(name = "service.oauth.host", value = "", label = "Oauth Host", description = "Oauth Athentication Server"),
#Property(name = "service.oauth.url", value = "/service/oauth/token", label = "Oauth URL", description = "Oauth Authentication URL relative to the host"),
#Property(name = "service.oauth.clientid", value = "", label = "Oauth Client ID", description = "Oauth client ID to use in the authentication procedure"),
#Property(name = "service.oauth.clientsecret", value = "", label = "Oauth Client Secret", description = "Oauth client secret to use in the authentication procedure"),
#Property(name = "service.oauth.granttype", value = "", label = "Oauth Grant Type", description = "Oauth grant type") })
public class OauthAuthentication {
...
#Activate
private void activate(ComponentContext context) {
Dictionary<String, Object> properties = context.getProperties();
host = OsgiUtil.toString(properties, PROPERTY_SERVICE_OAUTH_HOST,new String());
// Similarly get all values
url =
clientID =
clientSecret =
grantType =
authType = "Basic" + " "+ Base64.encode(new String(clientID + ":" + clientSecret));
}
public static void getAuthorizationToken(
try {
UserManager userManager = resourceResolver.adaptTo(UserManager.class);
Session session = resourceResolver.adaptTo(Session.class);
// Getting the current user
Authorizable auth = userManager.getAuthorizable(session.getUserID());
user = auth.getID();
password = ...
...
...
String serviceURL = (host.startsWith("http") ? "": protocol + "://") + host + url;
httpclient = HttpClients.custom().build();
HttpPost httppost = new HttpPost(serviceURL);
// set params
ArrayList<BasicNameValuePair> formparams = new ArrayList<BasicNameValuePair>();
formparams.add(new BasicNameValuePair("username", user));
formparams.add(new BasicNameValuePair("password", password));
formparams.add(new BasicNameValuePair("client_id", clientID));
formparams.add(new BasicNameValuePair("client_secret",clientSecret));
formparams.add(new BasicNameValuePair("grant_type",grantType));
UrlEncodedFormEntity postEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(postEntity);
// set header
httppost.addHeader("Authorization", authType);
response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (response.getStatusLine().getStatusCode() == 200) {
if (entity != null) {
object = new JSONObject(EntityUtils.toString(entity));
}
if (object != null) {
accessToken = object.getString("access_token");
////
}
}
}
I found the answer myself and thought I'd share the process I went through as well as the answer because it might help other people new to AEM.
How to find the cause of the error:
Go to CRXDE Lite.
Select console.
Then deselect the stop button to allow new console logs to appear (this is very counter-intuitive to me).
From here I was able to see the cause of the issue:
org.apache.sling.security.impl.ReferrerFilter Rejected empty referrer header for POST request to /oauth/token
Because postman does not place a referrer in the request header I had to tell Apache Sling to allow empty request headers.
To do this:
Go to /system/console/configMgr
Open the Apache Sling Referrer Filter Config
Select the Allow Empty check box
Good way to allow this to list the allowed hosts, otherwise this is against best practices for AEM security checklist.
Its fine for development environment not for production.
Related
Following https://developers.google.com/identity/sign-in/web/server-side-flow After getting the authorization code from JavaScript, and passing it to the server side, we indeed get an access token (and an ID token), but not the required refresh token.
There are many posts around but could not solve it yet.
Any suggestion how to get the refresh token?
thanks!
private String getResponseToken(GoogleClientSecrets clientSecrets,
String authCode) throws IOException {
try {
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
// "https://accounts.google.com/o/oauth2/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
authCode, //NOTE: was received from JavaScript client
"postmessage" //TODO: what's this?
).execute();
String accessToken = tokenResponse.getAccessToken();
String idToken = tokenResponse.getIdToken();
//TODO: not getting a refresh token... why?!
String refreshToken = tokenResponse.getRefreshToken();
Boolean hasRefreshToken = new Boolean(!(refreshToken == null));
LOGGER.warn("received refresh token: {}", hasRefreshToken);
LOGGER.debug("accessToken: {}, refreshToken: {}, idToken: {}", accessToken, refreshToken, idToken);
return accessToken;
}catch (TokenResponseException tre){...}
Gmail API only gives the refresh token the first time you ask for the users permission. (At least this is what happens to me).
Go to: https://myaccount.google.com/permissions?pli=1, remove the authorization to your app and run your code. You should receive the refresh token.
you should add the
AccessType = "offline"
You need to call the function
new GoogleAuthorizationCodeRequestUrl(...).setAccessType("offline")
or another syntax:
var authReq = new GoogleAuthorizationCodeRequestUrl(new Uri(GoogleAuthConsts.AuthorizationUrl)) {
RedirectUri = Callback,
ClientId = ClientId,
AccessType = "offline",
Scope = string.Join(" ", new[] { Scopes... }),
ApprovalPrompt = "force"
};
in Fiddler you should see the following request:
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/webmasters&redirect_uri=http://mywebsite.com/google/scapi/callback/&response_type=code&client_id=xxx&access_type=offline
see also here
More details about setAccessType can be found here
after finding how to use the Google APIs at the backend (documentation is somewhat partial..), the issue was fixed at the FrontEnd side by tweaking a parameter:
grantOfflineAccess({
- prompt: 'select_account'
+ prompt: 'consent'
HTH
Trying to retrieve the OAuth2.0 AccessToken using DotNetOpenAuth library, coded the same as below -
https://github.com/DotNetOpenAuth/DotNetOpenAuth/wiki/Security-scenarios
private static IAuthorizationState GetAccessTokenFromOwnAuthSvr()
{
var server = new AuthorizationServerDescription();
server.TokenEndpoint = new Uri("https://localhost/STS/OAuth/Token");
server.ProtocolVersion = DotNetOpenAuth.OAuth2.ProtocolVersion.V20;
var client = new UserAgentClient(server, clientIdentifier: "RP");
client.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter("data!");
var token = client.ExchangeUserCredentialForToken(
"Max Muster", "test123", new[] { "http://localhost/demo"});
return token;
}
This is not working as AuthZ Server returns error, complaining about the missing redirect_uri.
Going through few of the links here, saw that an AuthorizationState has the redirect_uri option, but could not figure out how to pass the AuthorizationState object in the ExchangeUserCredentialForToken request.
Is there a way to send the redirect_uri parameter or the AuthorizationState object as part of the ExchangeUserCredentialForToken request ?
Thanks in advance
--Abraham V K
I always get Bad Request 400 from IdentityServer3. I am trying for 3 days now but no luck :( Anyone could please tell me what am I doing wrong?
I am trying to access IdentityServer3 hosted by another vendor that I have no control. The vendor has asked us to implement Implement OAuth2 authentication with Bearer token. The vendor provided us with the Client ID, Client Secret and the URL to be used is http://www.xxxxxx.com/identity/connect/token
The vendor told us to use to request bearer token and use it in the request headers Authorization: Bearer
I can successfully obtain the bearer token from vendor. But when I call the
GET /api/profiles/myemailaddress#gmail.com I get Bad Request 400
Here is what I have done:
TokenClient client = new TokenClient("http://www.xxxxxx.com/identity/connect/token", "myclientid", "myclientsecret", AuthenticationStyle.PostValues);
var response = await client.RequestResourceOwnerPasswordAsync("myemailaddress#gmail.com", "mypassword", "profile"); // successfully gives me the token
i got the access token, now i want to use the token to request user profile:
var clienthttp = new HttpClient();
clienthttp.BaseAddress = new Uri("http://www.xxxxxx.com");
clienthttp.SetBearerToken(response.AccessToken);
var json = await clienthttp.GetStringAsync("http://www.xxxxxx.com/api/profiles/myemailaddress#gmail.com"); // error Bad Request 400
Additional Info:
"scopes_supported":["profile","offline_access"],
"claims_supported":[]
Thank you.
The vendor was expecting additional value in the header. Since my request was missing that additional value, they returned Bad Request. I had to modify my code to find the exact reason of bad request.
Here is the updated code, might be useful for someone:
var client = new HttpClient();
client.BaseAddress = new Uri("http://www.xxxxx.com");
client.SetBearerToken(response.AccessToken);
var callApiResponse = client.GetAsync("api/profiles/myemailaddress#gmail.com").Result;
string tokenresponse = callApiResponse.StatusCode.ToString();
string clientresult = callApiResponse.Content.ReadAsStringAsync().Result;
tokenresponse: "Bad Request 400"
clientresult: "Missing CompanyID in the header"
Then I knew that they also expect companyid in the header so I added it. then all was good.
client.DefaultRequestHeaders.Add("CompID", "xxxxxx");
I had a similar error (Response status code does not indicate success: 400 (Bad Request)) for different resource not identity server. i manage to resolve that using FormUrlEncodedContent
Refer below code
using (HttpClient client = new HttpClient())
{
string baseUrl = "https://*******.com/****"
Dictionary<string, string> jsonValues = new Dictionary<string, string>();
jsonValues.Add("username", "******");
jsonValues.Add("password", "******");
var contenta = new FormUrlEncodedContent(jsonValues);
var response = await client.PostAsync(baseUrl, contenta);
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync();
if (data != null)
{
Console.WriteLine(data);
}
}
}
I am building an OAuth service provider using DotNetOpenAuth, and to test it I have modified the sample wcf consumer to simply call a plain http endpoint. The token request works fine, but when I request access to a protected resource, I get the following protocol execption:
The following required parameters were missing from the DotNetOpenAuth.OAuth.Messages.AuthorizedTokenRequest message: oauth_verifier
When I look at the log output on my service provider I see this:
Error while performing basic validation of AuthorizedTokenRequest with these message parts:
oauth_token: pgzjBIs0pKCeDIcaIinyrV5Jhi0=
oauth_consumer_key: sampleconsumer
oauth_nonce: TM0Rc8kg
oauth_signature_method: HMAC-SHA1
oauth_signature: zmpxK5c69n1VzTEEcrnnd4e+qYI=
oauth_version: 1.0
oauth_timestamp: 1305067751
Notice the oauth_version: 1.0, even though I have specified ProtocolVersion.V10a when I create the consumer.
If I specify ProtocolVersion.V10 on both sides I get this exception:
Expected message DotNetOpenAuth.OAuth.Messages.AccessProtectedResourceRequest but received DotNetOpenAuth.OAuth.Messages.AuthorizedTokenRequest instead.
Here is the consumer code to get the token (this is straight from the sample code):
WebConsumer consumer = this.CreateConsumer();
UriBuilder callback = new UriBuilder(Request.Url);
callback.Query = null;
string[] scopes = (from item in this.scopeList.Items.OfType<ListItem>()
where item.Selected
select item.Value).ToArray();
string scope = string.Join("|", scopes);
var requestParams = new Dictionary<string, string> { { "scope", scope } };
var response = consumer.PrepareRequestUserAuthorization(callback.Uri, requestParams, null);
consumer.Channel.Send(response);
Here is my consumer code that is failing:
var accessToken = Session["WcfAccessToken"] as string;
var consumer = CreateConsumer();
var serviceEndpoint = new MessageReceivingEndpoint("https://mymachine/test/getUserName", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest);
var httpRequest = consumer.PrepareAuthorizedRequest(serviceEndpoint, accessToken);
var httpResponse = httpRequest.GetResponse();
In my service provider I call serviceProvider.ReadProtectedResourceAuthorization(); and it fails with the exception I mentioned above.
Any ideas what I am doing wrong?
This was a silly mistake on my part, I was returning the wrong TokenType, from my IServiceProviderTokenManager. The correct logic is shown in the service provider sample, and looks something like this:
if (tokenObject.State == TokenAuthorizationState.AccessToken)
return TokenType.AccessToken;
return TokenType.RequestToken;
I've been able setup the oAuth calls to get the users access Token following a couple blog posts:
http://sudheerkovalam.wordpress.com/2010/08/28/a-windows-phone-7-twitter-application-part-1/
and
:/byatool.com/c/connect-your-web-app-to-twitter-using-hammock-csharp/comment-page-1/#comment-9955
But I'm having problems sending a status update. I can't find any examples so I may not be setting the proper values. Here's the code which keeps returning: "Could not authenticate with OAuth."
private void Tweet()
{
var credentials = new OAuthCredentials
{
Type = OAuthType.ProtectedResource,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
ConsumerKey = TwitterSettings.ConsumerKey,
ConsumerSecret = TwitterSettings.ConsumerKeySecret,
Token = _settings.AccessToken,
TokenSecret = _settings.AccessTokenSecret,
Version = TwitterSettings.OAuthVersion,
};
var client = new RestClient
{
Authority = "http://twitter.com/oauth",
Credentials = credentials,
HasElevatedPermissions = true
};
var request = new RestRequest
{
Path = "/statuses/update.json",
Method = WebMethod.Post
};
request.AddParameter("status", TwitterTextBox.Text);
client.BeginRequest(request, new RestCallback(TwitterPostCompleted));
}
private void TwitterPostCompleted(RestRequest request, RestResponse response, object userstate)
{
Dispatcher.BeginInvoke(() => MessageBox.Show(response.Content));
}
thanks for any help,
Sam
Ah figured it out finally I was using the wrong URL need to use:
Authority = "http://api.twitter.com" and not: "http://twitter.com/oauth"
Just in case other people find this I've written a blog post on using OAth with Hammock for Twitter. Might be of use to some people!