I am coding a social management tool. I need infinite access token for Facebook.
First please tell me if I understand this correctly;
Access Token the reason we get is for to be able to have users account permissions to application, and this Access Token has 60 days to expire. but even after that expired still user accounts permissions on application only we don't have permission to reach it?
Can I renew access token after expired? I tried something like this;
void GetToken()
{
try
{
WebClient webClient = new WebClient();
String fb_exchange_token = null;
fb_exchange_token = ds.Tables[0].Rows[0]["token"].ToString();
String newToken = fb_exchange_token;
var fb = new FacebookClient();
try
{
dynamic result = fb.Get("oauth/access_token", new
{
client_id = client_id,
client_secret = client_secret,
grant_type = "fb_exchange_token",
fb_exchange_token = fb_exchange_token
});
}
catch (Exception ex)
{
if (ex.Message.Contains("expired"))
{
dynamic result = fb.Get("oauth/access_token", new
{
client_id = client_id,
client_secret = client_secret,
grant_type = "client_credentials",
fb_exchange_token = fb_exchange_token
});
newToken = (string)result.access_token;
if (newToken != fb_exchange_token)
SqlHelper.ExecuteNonQuery(ConnectionString, "SocialTokenUpdate", ds.Tables[0].Rows[0]["SocialId"].ToString(), newToken);
}
DataProvider.ExceptionLogAdd("xx.aspx", "GetToken", ex.Message);
}
Session["Token"] = newToken;
Session["FBPageId"] = ds.Tables[0].Rows[0]["SocailFBId"].ToString();
}
catch (Exception ex)
{
DataProvider.ExceptionLogAdd("xx.aspx", "GetToken", ex.Message);
}
}
If you want to generate new accessToken automatically means Let the application open the first screen where user gives access to Application. Then it will generate new accessToken. If accessToken is Expired get it in OAuthException and redirect to front screen.
Related
my question is how to pass username and password from the C# client(xamarin forms) to server's API? if details are correct then the client will get whole product list from webapi(URL).and bind all the details to a listview.I want to get the member details after the success of response code.
the client will send username password from login page to server's API. if server's webapi check whether the details matched with the database, if not, don't let it get product list.
here is the code in loginservices for login(xamarin forms)
public async Task GetData(string username,string password)
{
//string detail = new UserDetails();
UserDetails userDetails = new UserDetails();
// List<UserDetails> detail = new List<UserDetails>();
try
{
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("Username", username));
values.Add(new KeyValuePair<string, string>("Password", password));
var content = new FormUrlEncodedContent(values);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("nl-NL"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsync("http://192.168.1.50/Accounts/Authenticate", content);
return response.IsSuccessStatusCode;
};
}
catch (Exception ex)
{
throw ex;
}
}
here is the code for web api---
public async Task ValidateUser([FromBody] Credentials credentials)
{
using (DemoAPPEntities entities = new DemoAPPEntities())
{
var result = await entities.MemberDetails.Where(x => x.UserName == credentials.UserName && x.Password == credentials.Password).SingleOrDefaultAsync();
if (result == null)
{
return NotFound();
}
return Ok(entities.MemberDetails);
}
}
I need to separate the authentication phase from Google's Api creation, but it's very difficult (for me) to make it possible.
This is very important because I am creating a REST API that should receive the authorization tokens previously acquired and not the credentials directly from its users for security reasons, because with tokens I can set a lifetime limit as specified in RFC 6750.
I have the following code:
public class Main {
public static void main(String[] args) {
// Reads the JSON credential file provided by Google
String jsonContent = readJson(args[1]);
// Pass the credential content
GoogleComputeEngineApi googleApi =
createApi(jsonContent);
}
public static GoogleComputeEngineApi createApi(final String jsonCredentialContent) {
try {
Supplier<Credentials> credentialSupplier = new GoogleCredentialsFromJson(
jsonCredentialContent);
ComputeServiceContext context = ContextBuilder
.newBuilder("google-compute-engine")
.credentialsSupplier(credentialSupplier)
.buildView(ComputeServiceContext.class);
Credentials credentials = credentialSupplier.get();
ContextBuilder contextBuilder = ContextBuilder
.newBuilder(GoogleComputeEngineProviderMetadata.builder()
.build())
.credentials(credentials.identity, credentials.credential);
Injector injector = contextBuilder.buildInjector();
return injector.getInstance(GoogleComputeEngineApi.class);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
}
}
Below is a fake code with my needs:
public class Main {
public static void main(String[] args) {
String jsonCredentialContent = readJson(args[1]);
String oauthToken = "";
// First acquires the OAuth token
if(getAuthenticationType("google-compute-engine").equals("oauth")) {
oauthToken = getTokenForOAuth(jsonCredentialContent);
}
// Creates the Api with the previously acquired token
GoogleComputeEngineApi googleApi =
createApi(oauthToken);
}
[...]
}
You can directly use the jclouds OAuth API to get the bearer token, as follows:
GoogleCredentialsFromJson credentials = new GoogleCredentialsFromJson(jsoncreds);
AuthorizationApi oauth = ContextBuilder.newBuilder("google-compute-engine")
.credentialsSupplier(credentials)
.buildApi(AuthorizationApi.class);
try {
long nowInSeconds = System.currentTimeMillis() / 1000;
Claims claims = Claims.create(
credentials.get().identity, // issuer
"https://www.googleapis.com/auth/compute", // write scope
"https://accounts.google.com/o/oauth2/token", // audience
nowInSeconds + 60, // token expiration (seconds)
nowInSeconds // current time (secods)
);
Token token = oauth.authorize(claims);
System.out.println(token);
} finally {
oauth.close();
}
Once you have the Bearer access token you can create the jclouds context with it as follows:
// Override GCE default Oauth flow (JWT) by the Bearer token flow
Properties overrides = new Properties();
overrides.put(OAuthProperties.CREDENTIAL_TYPE, CredentialType.BEARER_TOKEN_CREDENTIALS.toString());
// It is important to set the proper identity too, as it is used to resolve the GCE project
ComputeServiceContext ctx = ContextBuilder.newBuilder("google-compute-engine")
.overrides(overrides)
.credentials(credentials.get().identity, token.accessToken())
.buildView(ComputeServiceContext.class);
GoogleComputeEngineApi google = ctx.unwrapApi(GoogleComputeEngineApi.class);
I am trying to utilize AWS Cognito User Pools in my cross platform Xamarin app. I correctly begin registration for the user in the user pool (The user shows up in the user pool and the email with the verification code is sent). I can't seem to figure out the correct way to verify the users email to confirm them in the user pool. I keep getting NotAuthorizedException.
--------EDIT: The code blocks below have been updated to my latest attempts--------
Code for registering user:
public async Task<Exception> RegisterUserInUserPool(String sUsername, String sPassword, String sEmail)
{
AmazonCognitoIdentityProviderClient oClient = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(), Amazon.RegionEndpoint.USEast2);
CognitoUserPool oUserPool = new CognitoUserPool(sUserPoolID, sClientID, oClient);
try
{
await oUserPool.SignUpAsync(sUsername, sPassword, new Dictionary<string, string> { { "email", sEmail } }, null);
return null;
}
catch (Exception e)
{
return e;
}
}
My latest attempt at verifying the user:
public async Task<Exception> VerifyEmail(String sUsername, String sVerificationCode)
{
CognitoAWSCredentials oCreds = new CognitoAWSCredentials(sIdentityPoolID, Amazon.RegionEndpoint.USEast2);
AmazonCognitoIdentityProviderClient oClient = new AmazonCognitoIdentityProviderClient(oCreds, Amazon.RegionEndpoint.USEast2);
CognitoUserPool oUserPool = new CognitoUserPool(sUserPoolID, sClientID, oClient);
CognitoUser oCognitoUser = new CognitoUser(sUsername, sClientID, oUserPool, oClient);
try
{
await oCognitoUser.ConfirmSignUpAsync(sVerificationCode, false);
return null;
}
catch (Exception e)
{
return e;
}
}
EDIT: The updated code above for confirming user verification is returning a NotAuthorizedException exception that says "Unauthenticated access is not supported for this identity pool."
What are the correct settings for the user pool to allow this kind of confirmation? Is my code missing any steps?
Any help or clarification is appreciated!
CognitoIdentityServiceProvider SDK:
Use the confirmRegistration() or adminconfirmSignUp() functions.
Example Code
I am using the following code and it works well
AmazonCognitoIdentityProviderClient providerClient = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(), AWSSettings.AWS_REGION);
ConfirmSignUpRequest confirmRequest = new ConfirmSignUpRequest()
{
Username = username,
ClientId = AWSSettings.AWS_CLIENT_ID, //use your own client id
ConfirmationCode = code
};
return await providerClient.ConfirmSignUpAsync(confirmRequest);
Also, the AWS Cognito client app should not have the secretId and ADMIN_NO_SRP_AUTH should not be marked.
Thanks to everyone who took the time to answer. A combination of things led me to working code. I want to post the code that works for me as well as a few tips that I could've used. Hopefully it can help someone!
Register the user in the user pool:
public async Task<Exception> RegisterUserInUserPool(String sUsername, String sPassword, String sEmail)
{
AmazonCognitoIdentityProviderClient oClient = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(), Amazon.RegionEndpoint.USEast2);
CognitoUserPool oUserPool = new CognitoUserPool(sUserPoolID, sClientID, oClient);
try
{
await oUserPool.SignUpAsync(sUsername, sPassword, new Dictionary<string, string> { { "email", sEmail } }, null);
return null;
}
catch (Exception e)
{
return e;
}
}
Confirm the user's email:
public async Task<Exception> VerifyEmail(String sUsername, String sVerificationCode)
{
AmazonCognitoIdentityProviderClient oClient = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(), Amazon.RegionEndpoint.USEast2);
CognitoUserPool oUserPool = new CognitoUserPool(sUserPoolID, sClientID, oClient);
CognitoUser oCognitoUser = new CognitoUser(sUsername, sClientID, oUserPool, oClient);
try
{
await oCognitoUser.ConfirmSignUpAsync(sVerificationCode, false);
return null;
}
catch (Exception e)
{
return e;
}
}
A few tips:
Know the difference between AWS user pools and identity pools.
Check the spam folder for the verification code. (seems simple, but this had me going for a while)
The .NET AWS docs are useful for some things. (A little lacking overall in my opinion)
The next step to authenticate users and allow them to access AWS resources is to call StartWithSrpAuthAsync on the CognitoUser model.
Keep in mind this is all utilizing the AWSSDK.Extensions.CognitoAuthentication Nuget package.
I just wanted to add extra info, as this is the first stackoverflow option from google, for anyone struggling with cognito email verification.
If you are registering a user but NOT getting email verification links, check that you have set up an email forwarder.
On the Cognito User Pool Page goto:
App Integration > Domain Name: Enter a domain prefix here to allow verification emails to be sent.
This is the code that I have used to register a user and send a confirmation link.
public async Task<SignUpResponse> SignupUserAsync(CognitoUser user)
{
var region = "eu-west-2";
var provider = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(),
RegionEndpoint.GetBySystemName(region));
var signupRequest = new SignUpRequest
{
ClientId = _clientId,
Username = user.Email,
Password = user.Password
};
AttributeType emailAttribute = new AttributeType
{
Name = "email",
Value = user.Email
};
signupRequest.UserAttributes.Add(emailAttribute);
var newUser = provider.SignUpAsync(signupRequest);
return await newUser;
}
CognitoUser is a custom class that inherits from IdentityUser, found on a tutorial, I just copied it.
public class CognitoUser : IdentityUser
{
public string Password { get; set; }
public UserStatusType Status { get; set; }
}
Onto the next problem which I am sure isn't too far in the future. Aha
Hope it helps!
I have configured an ASOS OpenIdConnect Server using and an asp.net core mvc app that uses the "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0 and "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0". I have tested the "Authorization Code" workflow and everything works.
The client web app processes the authentication as expected and creates a cookie storing the id_token, access_token, and refresh_token.
How do I force Microsoft.AspNetCore.Authentication.OpenIdConnect to request a new access_token when it expires?
The asp.net core mvc app ignores the expired access_token.
I would like to have openidconnect see the expired access_token then make a call using the refresh token to get a new access_token. It should also update the cookie values. If the refresh token request fails I would expect openidconnect to "sign out" the cookie (remove it or something).
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = "myClient",
ClientSecret = "secret_secret_secret",
PostLogoutRedirectUri = "http://localhost:27933/",
RequireHttpsMetadata = false,
GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true,
ResponseType = OpenIdConnectResponseType.Code,
AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet,
Authority = http://localhost:27933,
MetadataAddress = "http://localhost:27933/connect/config",
Scope = { "email", "roles", "offline_access" },
});
It seems there is no programming in the openidconnect authentication for asp.net core to manage the access_token on the server after received.
I found that I can intercept the cookie validation event and check if the access token has expired. If so, make a manual HTTP call to the token endpoint with the grant_type=refresh_token.
By calling context.ShouldRenew = true; this will cause the cookie to be updated and sent back to the client in the response.
I have provided the basis of what I have done and will work to update this answer once all work as been resolved.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = "Cookies",
ExpireTimeSpan = new TimeSpan(0, 0, 20),
SlidingExpiration = false,
CookieName = "WebAuth",
Events = new CookieAuthenticationEvents()
{
OnValidatePrincipal = context =>
{
if (context.Properties.Items.ContainsKey(".Token.expires_at"))
{
var expire = DateTime.Parse(context.Properties.Items[".Token.expires_at"]);
if (expire > DateTime.Now) //TODO:change to check expires in next 5 mintues.
{
logger.Warn($"Access token has expired, user: {context.HttpContext.User.Identity.Name}");
//TODO: send refresh token to ASOS. Update tokens in context.Properties.Items
//context.Properties.Items["Token.access_token"] = newToken;
context.ShouldRenew = true;
}
}
return Task.FromResult(0);
}
}
});
You must enable the generation of refresh_token by setting in startup.cs:
Setting values to AuthorizationEndpointPath = "/connect/authorize"; // needed for refreshtoken
Setting values to TokenEndpointPath = "/connect/token"; // standard token endpoint name
In your token provider, before validating the token request at the end of the HandleTokenrequest method, make sure you have set the offline scope:
// Call SetScopes with the list of scopes you want to grant
// (specify offline_access to issue a refresh token).
ticket.SetScopes(
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess);
If that is setup properly, you should receive a refresh_token back when you login with a password grant_type.
Then from your client you must issue the following request (I'm using Aurelia):
refreshToken() {
let baseUrl = yourbaseUrl;
let data = "client_id=" + this.appState.clientId
+ "&grant_type=refresh_token"
+ "&refresh_token=myRefreshToken";
return this.http.fetch(baseUrl + 'connect/token', {
method: 'post',
body : data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
}
});
}
and that's it, make sure that your auth provider in HandleRequestToken is not trying to manipulate the request that is of type refresh_token:
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
if (context.Request.IsPasswordGrantType())
{
// Password type request processing only
// code that shall not touch any refresh_token request
}
else if(!context.Request.IsRefreshTokenGrantType())
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid grant type.");
return;
}
return;
}
The refresh_token shall just be able to pass through this method and is handled by another piece of middleware that handles refresh_token.
If you want more in depth knowledge about what the auth server is doing, you can have a look at the code of the OpenIdConnectServerHandler:
https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/master/src/AspNet.Security.OpenIdConnect.Server/OpenIdConnectServerHandler.Exchange.cs
On the client side you must also be able to handle the auto refresh of the token, here is an example of an http interceptor for Angular 1.X, where one handles 401 reponses, refresh the token, then retry the request:
'use strict';
app.factory('authInterceptorService',
['$q', '$injector', '$location', 'localStorageService',
function ($q, $injector, $location, localStorageService) {
var authInterceptorServiceFactory = {};
var $http;
var _request = function (config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
};
var _responseError = function (rejection) {
var deferred = $q.defer();
if (rejection.status === 401) {
var authService = $injector.get('authService');
console.log("calling authService.refreshToken()");
authService.refreshToken().then(function (response) {
console.log("token refreshed, retrying to connect");
_retryHttpRequest(rejection.config, deferred);
}, function () {
console.log("that didn't work, logging out.");
authService.logOut();
$location.path('/login');
deferred.reject(rejection);
});
} else {
deferred.reject(rejection);
}
return deferred.promise;
};
var _retryHttpRequest = function (config, deferred) {
console.log('autorefresh');
$http = $http || $injector.get('$http');
$http(config).then(function (response) {
deferred.resolve(response);
},
function (response) {
deferred.reject(response);
});
}
authInterceptorServiceFactory.request = _request;
authInterceptorServiceFactory.responseError = _responseError;
authInterceptorServiceFactory.retryHttpRequest = _retryHttpRequest;
return authInterceptorServiceFactory;
}]);
And here is an example I just did for Aurelia, this time I wrapped my http client into an http handler that checks if the token is expired or not. If it is expired it will first refresh the token, then perform the request. It uses a promise to keep the interface with the client-side data services consistent. This handler exposes the same interface as the aurelia-fetch client.
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';
import {AuthService} from './authService';
#inject(HttpClient, AuthService)
export class HttpHandler {
constructor(httpClient, authService) {
this.http = httpClient;
this.authService = authService;
}
fetch(url, options){
let _this = this;
if(this.authService.tokenExpired()){
console.log("token expired");
return new Promise(
function(resolve, reject) {
console.log("refreshing");
_this.authService.refreshToken()
.then(
function (response) {
console.log("token refreshed");
_this.http.fetch(url, options).then(
function (success) {
console.log("call success", url);
resolve(success);
},
function (error) {
console.log("call failed", url);
reject(error);
});
}, function (error) {
console.log("token refresh failed");
reject(error);
});
}
);
}
else {
// token is not expired, we return the promise from the fetch client
return this.http.fetch(url, options);
}
}
}
For jquery you can look a jquery oAuth:
https://github.com/esbenp/jquery-oauth
Hope this helps.
Following on from #longday's answer, I have had success in using this code to force a client refresh without having to manually query an open id endpoint:
OnValidatePrincipal = context =>
{
if (context.Properties.Items.ContainsKey(".Token.expires_at"))
{
var expire = DateTime.Parse(context.Properties.Items[".Token.expires_at"]);
if (expire > DateTime.Now) //TODO:change to check expires in next 5 mintues.
{
context.ShouldRenew = true;
context.RejectPrincipal();
}
}
return Task.FromResult(0);
}
I'm trying to use AWS IAM to generate temporary tokens for a mobile app. I'm using the AWS C# SDK.
Here's my code...
The token generating service
public string GetIAMKey(string deviceId)
{
//fetch IAM key...
var credentials = new BasicAWSCredentials("MyKey", "MyAccessId");
var sts = new AmazonSecurityTokenServiceClient(credentials);
var tokenRequest = new GetFederationTokenRequest();
tokenRequest.Name = deviceId;
tokenRequest.Policy = File.ReadAllText(HostingEnvironment.MapPath("~/policy.txt"));
tokenRequest.DurationSeconds = 129600;
var tokenResult = sts.GetFederationToken(tokenRequest);
var details = new IAMDetails { SessionToken = tokenResult.GetFederationTokenResult.Credentials.SessionToken, AccessKeyId = tokenResult.GetFederationTokenResult.Credentials.AccessKeyId, SecretAccessKey = tokenResult.GetFederationTokenResult.Credentials.SecretAccessKey, };
return JsonConvert.SerializeObject(details);
}
The client
var iamkey = Storage.LoadPersistent<IAMDetails>("iamkey");
var simpleDBClient = new AmazonSimpleDBClient(iamkey.AccessKeyId, iamkey.SecretAccessKey, iamkey.SessionToken);
try
{
var details = await simpleDBClient.SelectAsync(new SelectRequest { SelectExpression = "select * from mydomain" });
return null;
}
catch (Exception ex)
{
Storage.ClearPersistent("iamkey");
}
The policy file contents
{ "Statement":[{ "Effect":"Allow", "Action":"sdb:* ", "Resource":"arn:aws:sdb:eu-west-1:* :domain/mydomain*" } ]}
I keep getting the following error...
User (arn:aws:sts::myaccountid:federated-user/654321) does not have permission to perform (sdb:Select) on resource (arn:aws:sdb:us-east-1:myaccountid:domain/mydomain)
Notice that my policy file clearly specifies two things
region should be eu-west-1
allowed action is a wild-card, ie, allow everything
But the exception thrown claims that my user doesn't have permission to us-east-1
Any ideas as to why I'm getting this error?
Ok figured it out.
You have to set the region endpoint on your call to the service from the client.
So
var simpleDBClient = new AmazonSimpleDBClient(iamkey.AccessKeyId, iamkey.SecretAccessKey, iamkey.SessionToken, Amazon.RegionEndpoint.EUWest1);