I am using HTTP Trigger Azure Functions for getting/posting data on a third party API service. This API service needs firstly to authenticate and then get a Token for using another service. So , I want to cache the token. Problem is that once I deploy to Azure, it asks me for an "empty paramter" called S. Strange as it sounds. Every advice is welcome. Thanks! Cache code is :
/*
object cacheKeyDefault = MemoryCache.Default.Get($"{userId+usuario + canal + device}");
if (cacheKeyDefault == null)
{
//Genero póliza con el tiempo obtenido del Environment
CacheItemPolicy cip = new CacheItemPolicy()
{
AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddHours(double.Parse(tokenTimeout)))
};
//Genero Json de request para Token
RequestToken tokenRequest = JsonConvert.DeserializeObject<RequestToken>(requestBody);
string tokenrequestJs = JsonConvert.SerializeObject(tokenRequest);
log.LogInformation($"tokenrequestJs: {tokenrequestJs} ");
//Obtengo el Session Token
string responseTokenJs = RestAPI.Ejecutar(url + accionToken, Method.POST, tokenrequestJs);
log.LogInformation("Response Token: " + responseTokenJs);
//Lo agrego dinámicamente
dynamic tokenResponseDynamic = JsonConvert.DeserializeObject(responseTokenJs);
dynamic SessionToken = tokenResponseDynamic.SessionToken;
string SessionTokenString = SessionToken;
reqBody.Btinreq.Token = SessionToken;
MemoryCache.Default.Set($"{userId+usuario +canal+device}", SessionTokenString, cip);
}
else
{
dynamic SessionToken = cacheKeyDefault;
reqBody.Btinreq.Token = SessionToken;
}
Related
I am trying to upload signature for a user using eSign SDK. I have referred this link for creating a signature for user. Below is my C# code:
var usersList = usersApi.List(Constants.accountId);
ApiClient apiClient1 = new ApiClient(Constants.basePath);
apiClient1.Configuration.AddDefaultHeader("Authorization", "Bearer " + Constants.userAccessToken);
UsersApi usersApi1 = new UsersApi(apiClient1.Configuration);
if (usersList != null && usersList.Users != null && usersList.Users.Any())
{
var activeUser = usersList.Users.FirstOrDefault(x => x.Email == data["UserEmail"] && x.UserStatus.Equals("active", comparisonType: StringComparison.CurrentCultureIgnoreCase));
if (activeUser != null)
{
UserSignature userSignature = new UserSignature()
{
IsDefault = "true",
SignatureInitials = "TU",
SignatureName = "Test User",
ImageBase64 = Convert.ToBase64String(ReadContent(Constants.eSignName))
};
List<UserSignature> userSignatures = new List<UserSignature>() { userSignature };
UserSignaturesInformation userSignaturesInformation = new UserSignaturesInformation()
{
UserSignatures = userSignatures
};
//Create Signature
var signResult = usersApi1.CreateSignatures(Constants.accountId, activeUser.UserId, userSignaturesInformation);
}
}
After this call, the signature gets created. However, the user's signature image(passed in ImageBase64 parameter) is not added to the created signature . The access token I have used here is from DocuSign Token generator tool from User profile. Need help in figuring out what's wrong with this request.
In order to take action on behalf of a different user (in your case, you want to generate and upload a signature image for a different user) you have to impersonate them when you authenticate using JWT. You have to have their userID (a GUID) as part of your API call and the user must consent to the app making call on their behalf at least once.
I am trying to get a msi token for a specific User defined identity. Our app service has 2 user defined identities and I want a token on behalf of one of the user assigned identity.
Here is the code:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/&object_id=<ObjectId>&client_id=<clientId>");
req.Headers["Metadata"] = "true";
req.Method = "GET";
try
{
// Call /token endpoint
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
// Pipe response Stream to a StreamReader, and extract access token
StreamReader streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
Dictionary<string, string> list =
JsonConvert.DeserializeObject<Dictionary<string, string>>(stringResponse);
string accessToken = list["access_token"];
System.IO.File.WriteAllText(#".\Log.txt", accessToken);
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
System.IO.File.WriteAllText(#".\Log.txt", errorText);
throw;
}
It is deployed in an azure app service. When I hit this section I see this error:
An attempt was made to access a socket in a way forbidden by its access permissions
I tried connecting to http://169.254.169.254 to get the token using kudu console. But this endpoint does not seem to accessible there.
I did try to use AzureServiceTokenProvider from Microsoft.Azure.Services.AppAuthentication for generating msi token but could not find any documentation about how to use it for multiple user assigned identities.
Edit:
Update 1:
I tried to use endpoint from MSI_ENDPOINT environment variable instead of 169.254.169.254. But it looks like MSI_ENDPOINT value is not set when I run the app service.
Here is the code I have tried:
var endpoint = Environment.GetEnvironmentVariable("MSI_ENDPOINT");
string apiVersion = "2018-02-01";
string resource = "https://management.azure.com/";
string objectId = "<objectid>";
string clientId = "<clientId>";
// Build request to acquire managed identities for Azure resources token
//HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
// "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/&object_id=4aef1720-b3b1-4935-8d68-e330508907fa&client_id=558ecc75-8697-4419-bab9-aa2c87043cfd");
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
String.Format(
"{0}?resource={1}&api-version={2}&object_id={3}&client_id={4}",
endpoint,
resource,
apiVersion,
objectId,
clientId));
req.Headers["Metadata"] = "true";
req.Method = "GET";
try
{
// Call /token endpoint
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
// Pipe response Stream to a StreamReader, and extract access token
StreamReader streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
Dictionary<string, string> list =
JsonConvert.DeserializeObject<Dictionary<string, string>>(stringResponse);
string accessToken = list["access_token"];
System.IO.File.WriteAllText(#".\Log.txt", accessToken);
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
string log = "MSI_ENDPOINT : " + endpoint + "\n";
log += ("ErrorText : " + errorText + "\n");
System.IO.File.WriteAllText(#".\Log.txt", errorText);
throw;
}
Firstly, this link How to use managed identities for App Service and Azure Functions provides good documentation specific to MSI for App Services.
Here is quick sample code.. to get token for a specific user assigned managed service identity as you've asked in your question.
resource - The AAD resource URI of the resource for which a token should be obtained.
apiversion - The version of the token API to be used. "2017-09-01" is currently the only version supported.
clientId - The ID of the user-assigned identity to be used. If omitted, the system-assigned identity is used.
public static async Task<HttpResponseMessage> GetToken(string resource, string apiversion, string clientId)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
return await client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}&clientid={3}", Environment.GetEnvironmentVariable("MSI_ENDPOINT"), resource, apiversion,clientId));
}
Overall I see a few changes that you should notice in the sample code above:
Make use of MSI_ENDPOINT to construct URL at runtime
parameter should be clientid and not client_id
parameter object_id is not needed
api version should be "2017-09-01" as documentation in above link says that's the only one supported.
About your issue with MSI_ENDPOINT value not being set when you run the app service, please take a look at this note from same link in Microsoft Docs
Screenshot from documentation that is relevant for all parameters used
I am investigating how to possibly authenticate to a Kubernetes 1.13 cluster with OpenID Connect and Keycloak. I am new to this area.
This YouTube video ("Use Open ID Connect for Kubernetes API server") accomplishes part of what I want. An id token is initially obtained by making a HTTP request (with curl) to Keycloak citing grant type password. The resulting token is then subsequently used in further HTTP requests to the Kubernetes API. This works but has the disadvantage that clients directly handle users' permanent credentials.
Would it not be better if the token were issued by a secure web page that also required authentication via Keycloak (this time with grant type authorization code) and did nothing else but displaying a new token? Such tokens (transient credentials) could then e.g. be manually copied into kubeconfigs for further use?
Does Keycloak provide such interactive web pages (next to the REST endpoints for obtaining tokens programatically) or is this out of scope? If the second, are there other standard components for such tasks?
UPDATE This illustration from the Kubernetes documentation perhaps makes more clear what I am seeking. In step 1 a user should log into the Identity provider to obtain tokens which can then be configured into kubectl. Does Keycloak support this step, i.e. offer a web page where users could log in to obtain their tokens?
If I am able to understand your question ,so you want to get the accesstoken via Java code so here is code you can write and call
String obtainAccessToken = obtainAccessToken(username, password);
putRequest.addHeader("Authorization", "bearer " + obtainAccessToken);
putRequest.addHeader("content-type", MediaType.APPLICATION_JSON);
Here is the method you should call
public String obtainAccessToken(String UserName, String pwd)
{
AuthzClient authzClient = AuthzClient.create(configuration);
AccessTokenResponse accessTokenResponse = authzClient.obtainAccessToken(UserName, pwd);
String token = accessTokenResponse.getToken();
return token;
}
Here is the get realm method
public Response getAllRealms() {
ObjectMapper mapper = JacksonObjectMapperProvider.getObjectMapper();
CloseableHttpResponse response = null;
List<SureRealmRepresentation> realmList = new ArrayList<SureRealmRepresentation>();
int status;
try {
String urlGetAllRealms = URL + "/admin/realms";
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet getRequest = new HttpGet(urlGetAllRealms);
String obtainAccessToken = obtainAccessToken(username, password);
getRequest.addHeader("Authorization", "bearer " + obtainAccessToken);
getRequest.addHeader("content-type", MediaType.APPLICATION_JSON);
response = httpclient.execute(getRequest);
status = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());
if (status == 200) {
RealmRepresentation[] realmArray = mapper.readValue(responseBody, RealmRepresentation[].class);
}
catch (Exception e) {
if (e instanceof Exception) {
throw (Exception) e;
} else {
throw ErrorHandler.wrap(new Exception("EroorType : "+ e.toString()));
}
}
I have an assignment to access a web API to get a token and use this token as an input to call another web API. Token expires after 60 seconds so I have to again call first web API to get new token and then use this new token to access my second web API.
In normal scenario it is working fine but some time second API takes more time to execute and token expires.
I have used HttpClient class to call web API. How should I handle token expiration time constraint before calling another web API. I want to make it more generic.
I have written following code.
public override bool CallAPI()
{
try
{
bool isSPAdded = false;
int counter = 0;
do
{
Uri uri = new Uri(_settings.HostUrl + "/auth/realms/master/protocol/openid-connect/token");
var requestContent = string.Format("username={0}&password={1}&client_id=admin-cli&grant_type=password", _settings.UserName, _settings.Password);
StringContent _requestContent = new StringContent(requestContent, Encoding.UTF8, "application/x-www-form-urlencoded");
_response = _client.PostAsync(uri, _requestContent).Result;
if (_response.IsSuccessStatusCode)
{
Uri uri = new Uri(String.Format(_settings.HostUrl + "/auth/admin/realms/{0}/clients", Node.OrgId));
_requestContent = new StringContent(response.Content.ReadAsStringAsync().Result, Encoding.UTF8, "application/json");
_response = _client.PostAsync(uri, _requestContent).Result;
if (_response.IsSuccessStatusCode)
{
isSPAdded = _response.IsSuccessStatusCode;
break;
}
}
counter++;
} while (_response.StatusCode == HttpStatusCode.Unauthorized && _response.Content.ReadAsStringAsync().Result == "Bearer" && counter < 2);
return isSPAdded;
}
}
I have used do while loop to retry it if token expires.
Please suggest the best approach for same.
I am building a Universal Windows Platform (UWP) app that uses the Azure App Service Mobile App backend as well as the user's OneDrive account. I have 2 requirements for authentication:
If the user is logged in to their UWP device with a Microsoft account (e.g. Windows 10) then I don't want them to be presented with a login prompt (i.e. Single Sign On, re-using their Microsoft account credentials).
I want to have a single authentication event across Azure & OneDrive, i.e. the user authorises once and I re-use that token for both services.
I did this in Windows Phone 8 with an Azure Mobile Service by logging in with the Live SDK and then passing the returned token to the MobileServiceClient.LoginAsync() method, however I can't get this to work in UWP with an Azure Mobile App. When I call that same method I receive a 401 Unauthorised response.
I have associated my UWP app with the store and set up the
application at the Microsoft Account Developer Centre, including
adding the redirect URI from the Azure Mobile App.
I have set up the Azure App Service Mobile App, including adding the
Client ID & Secret from the Microsoft Account Developer Centre.
I have tried numerous ways to retrieve the token, including the
OnlineIdAuthenticator, WebAuthenticationCoreManager and
WebAuthenticationBroker. None has worked so far.
I currently use the following code in a class LiveAuthenticationService to retrieve an access token:
public async Task<bool> LoginAsync()
{
AccessToken = null;
bool success = false;
OnlineIdAuthenticator onlineIdAuthenticator = new OnlineIdAuthenticator();
EventWaitHandle waithandle = new ManualResetEvent(false);
OnlineIdServiceTicketRequest serviceTicketRequest = new OnlineIdServiceTicketRequest(scopes, "DELEGATION");
UserIdentity result = await onlineIdAuthenticator.AuthenticateUserAsync(serviceTicketRequest);
if (!string.IsNullOrWhiteSpace(result?.Tickets[0]?.Value))
{
currentUserId = result.SafeCustomerId;
AccessToken = result.Tickets[0].Value;
success = true;
waithandle.Set();
}
else
{
await logger.LogErrorAsync("Error signing in to Microsoft Live",
new Dictionary<string, string> { { "errorCode", result?.Tickets[0]?.ErrorCode.ToString() } });
}
waithandle.WaitOne(10000); //10 second timeout
return success;
}
And then this to attempt to login to my Azure Mobile App with that token, which uses LiveAuthenticationService from above:
private async Task RefreshUserIdAndAccessToken()
{
try
{
var tcs = new TaskCompletionSource<MobileServiceUser>();
var authService = new LiveAuthenticationService();
await UiDispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
try
{
await authService.LoginAsync();
var jsonAuthenticationToken = JObject.Parse(#"{""authenticationToken"": """ + authService.AccessToken + #"""}");
tcs.SetResult(await mobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, jsonAuthenticationToken));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
var user = await tcs.Task;
currentUserId = user.UserId;
AccessToken = user.MobileServiceAuthenticationToken;
}
catch (Exception ex)
{
await logger.LogExceptionAsync(ex,
Constants.LOGGING_DATAKEY_REFRESHACCESSTOKENFAILURE,
currentUserId);
currentUserId = null;
AccessToken = null;
}
}
As stated this results in a 401 Unauthorised response from Azure. I have run Fiddler and the request seems to be correct, the expected authentication token is included in a JSON payload with the request.
UPDATE
One thing I can see is that the token issued by the code above is almost 900 characters long, all in the form YnElFkAAcK8bRSQab/FK+PT5n/wA4CPU..., while the token issued if I let Azure Mobile App handle the authentication, i.e. call MobileServiceClient.LoginAsync() without passing a token, is only about 350 characters long and in the form hbGciOi.eyJmdWWxsIiwiRGJn... (notice the period towards the beginning).
This issue is really causing me problems now. I can't release the app without the authentication working and I can't figure out how to fix it. Any help will be appreciated.
This was a tough one for me to solve as I was facing this problem too.
The most important part is the OnlineIdServiceTicketRequest the request should look like this:
var mobileServicesTicket = new OnlineIdServiceTicketRequest("https://yourmobileservice.azure-mobile.net/", "JWT");
Note that we are specifying your endpoint and also requesting a JWT token instead of delegation. This will get the 350ish character token you were looking for.
Here is a full code sample of what I'm doing:
public async Task<bool> LoginAsync()
{
var authenticator = new Windows.Security.Authentication.OnlineId.OnlineIdAuthenticator();
var mobileServicesTicket = new Windows.Security.Authentication.OnlineId.OnlineIdServiceTicketRequest("https://yourendpoint.azure-mobile.net/", "JWT");
var ticketRequests = new List<OnlineIdServiceTicketRequest>() { mobileServicesTicket };
var authResult = await authenticator.AuthenticateUserAsync(ticketRequests, CredentialPromptType.PromptIfNeeded);
if ((authResult.Tickets.Count == 1) && (authResult.Tickets[0].ErrorCode == 0))
{
var accessToken = authResult.Tickets[0];
var res = await _mobileServiceClient.LoginWithMicrosoftAccountAsync(accessToken.Value);
return true;
}
else
{
return false;
}
}
_mobileServiceClient is injected into the class and is a reference to Microsoft.WindowsAzure.MobileServices.MobileServiceClient object within the WindowsAzure.MobileServices library.
I actually ended up writing a blog article about this problem here http://jshapland.com/single-sign-on-with-azure-mobile-services-in-a-uwp-app/