I am developing an authentication service for my web based java application using Azure AD OpenID connect framework. I am referring to adal4j-1.2.0.jar
The authentication is happening as per the behavior. I am getting the JWT claims and able to validate it.
But when 60 mins of session timeout occurs and I am trying to get new token claims using refresh token, the new tokens are not Signed JWT. They are Plain JWT.
I am using below call to acquire token using my initial refresh token which I am caching.
acquireTokenByRrefreshToken(refreshtoken, credential,null,null)
For validation of token, I am using the code as below
IDtokenValidator validator = new IDTokenValidator(issuer,clientID, JWSAlgo,URL)
validator.validate(idToken, exoectedNoounce); //this line throws badjwtexception signed ID token expected
Can anyone help me to understand how can I redeem the refresh token to get new Signed tokens. Or after redeeming the token, the new tokens are always Plain JWT.
I believe ,you are using implicit grant flow to get token.You are getting token from authorization end point.In this flow ,you will not get refresh token.Either you need to get new token after session expire or create a hidden frame which can get token before session expire.
You could refer to the official doc to acquire access token and refresh token by code grant flow.
Actually,methods in adal4j are implemented via HTTP REST API so that you could refer to the code below to request AuthorizationCode.
public static void getAuthorizationCode() throws IOException {
String encoding = "UTF-8";
String params = "client_id=" + clientId
+ "&response_type=" + reponseType
+ "&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F"
+ "&response_mode=query"
+ "&resource=https%3A%2F%2Fgraph.windows.net"
+ "&state=12345";
String path = "https://login.microsoftonline.com/" + tenantId + "/oauth2/authorize";
byte[] data = params.getBytes(encoding);
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(data.length));
conn.setConnectTimeout(5 * 1000);
OutputStream outStream = conn.getOutputStream();
outStream.write(data);
outStream.flush();
outStream.close();
System.out.println(conn.getResponseCode());
System.out.println(conn.getResponseMessage());
BufferedReader br = null;
if (conn.getResponseCode() != 200) {
br = new BufferedReader(new InputStreamReader((conn.getErrorStream())));
} else {
br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
}
System.out.println("Response body : " + br.readLine());
}
Then you could get access token using the AuthorizationCode you got and get refresh code using the code below.
public static void getToken(String refreshToken) throws IOException {
String encoding = "UTF-8";
String params = "client_id=" + clientId + "&refresh_token=" + refreshToken
+ "&grant_type=refresh_token&resource=https%3A%2F%2Fgraph.windows.net";
String path = "https://login.microsoftonline.com/" + tenantId + "/oauth2/token";
byte[] data = params.getBytes(encoding);
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(data.length));
conn.setConnectTimeout(5 * 1000);
OutputStream outStream = conn.getOutputStream();
outStream.write(data);
outStream.flush();
outStream.close();
System.out.println(conn.getResponseCode());
System.out.println(conn.getResponseMessage());
BufferedReader br = null;
if (conn.getResponseCode() != 200) {
br = new BufferedReader(new InputStreamReader((conn.getErrorStream())));
} else {
br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
}
System.out.println("Response body : " + br.readLine());
}
Hope it helps you.
Related
For an email account of my organization, we must perform some post-processing of the emails that arrive at the inbox through a cron. Is it possible to perform the authentication flow from a cron without having to enter the credentials in the browser?
What kind of application should it be (single-tenant, multi-tenant, etc.)? What kind of permissions should it have? What authentication flow should I use (client credentials, authorization code, etc)? Can it be done through Python's MSAL?
Thank you in advance!
Based on the SO reference,
We do have a code to getAccessToken() that successfully returns an access token
private String getUserNamesFromGraph() throws Exception {
String bearerToken = "Bearer "+getAccessToken();
String url = "https://graph.microsoft.com/v1.0/users";
String returnData = null;
try {
URL apiURL = new URL(url);
URLConnection con = apiURL.openConnection();
con.setRequestProperty("Authorization", bearerToken);
con.setRequestProperty("Content-Type", "application/json");
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
returnData = response.toString();
System.out.println(returnData);
} catch(Exception e) {
System.out.println(e);
}
return returnData;
}
private String getAccessToken() throws Exception {
String url = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
URL obj = new URL(url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
// header
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "eTarget API");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
String urlParameters = "client_id=***
APPLICATION ID FROM APPLICATION REGISTRATION PORTAL ***&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret=***
APPLICATION SECRET FROM APPLICATION REGISTRATION PORTAL ***&grant_type=client_credentials";
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
System.out.println("\nSending 'POST' request to URL : " + url);
System.out.println("Post parameters : " + urlParameters);
System.out.println("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
//print result
String returnData = response.toString();
System.out.println(returnData);
Map jsonTokenData = new Gson().fromJson(returnData, Map.class);
String accessToken = (String)jsonTokenData.get("access_token");
//System.out.println(accessToken);
return accessToken;
}
Permissions:
The application to read the users it has to have an explicitly granted User.Read.All application permission along with admin consent.
A daemon application without user interaction, you have to look at the application permissions and not delegated permissions.
Post all changes, you may use https://jwt.ms to check your access token
SO Reference: java - Microsoft Graph 401 Unauthorized with access token - Stack Overflow
Ref: https://developers.docusign.com/platform/auth/authcode/authcode-get-token/
I am having an issue on Step 2: Obtain Access Token
I am trying to get an access token. However I am getting the following error:
{
"error": "invalid_grant",
"error_description": "unsupported_grant_type"
}
whether its using c# code or using PostMan I am getting the error above.
In PostMan
URL: https://account-d.docusign.com/oauth/token
Method: Post
Headers
Authorization: BASIC BASE64_COMBINATION_OF_INTEGRATION_AND_SECRET_KEYS
Content_Type: application/json;charset=utf-8
Body: I tried form-data, x.www.form-urlencoded, raw... all are the same
grant_type: authorization_code
code: My_AUTHORIZATION_CODE
I also tried getting the access token in the call back page when I get the Authorization Code.
protected void Page_Load(object sender, EventArgs e)
{
var url = ConfigurationManager.AppSettings["DocuSign.TokenEndPoint"];
var data = $"grant_type=authorization_code=&{Request.QueryString["Code"]}";
WebRequest req = WebRequest.Create(url);
req.Method = "POST";
req.ContentLength = data.Length;
req.ContentType = "application/json; charset=UTF-8";
UTF8Encoding enc = new UTF8Encoding();
var code64 = Convert.ToBase64String(enc.GetBytes($"{ConfigurationManager.AppSettings["DocuSign.ClientId"]}:{ConfigurationManager.AppSettings["DocuSign.ClientSecret"]}"));
req.Headers.Add("Authorization", "Basic " + code64);
using (Stream ds = req.GetRequestStream())
{
ds.Write(enc.GetBytes(data), 0, data.Length);
}
WebResponse wr = req.GetResponse();
Stream receiveStream = wr.GetResponseStream();
StreamReader reader = new StreamReader(receiveStream, Encoding.UTF8);
string content = reader.ReadToEnd();
Response.Write(content);
}
Your content_type should be application/x-www-form-urlencoded
the following line was wrong:
var data = $"grant_type=authorization_code=&{Request.QueryString["Code"]}";
Changed to
var data = $"grant_type=authorization_code&code={Request.QueryString["Code"]}";
I was missing code=
I would like to programatically get and create Web Apps under App Services in Windows Azure.
How to create it by using its REST API?
As far as I know, if you want to use REST API to get and create Web App, you need generate a access token. To generate access token we need talentID, clientId, clientSecret. We need to assign application to role, after that then we can use token in common. More information about how to assign application to role please refer to the article .This blog also has more detail steps to get AceessToken.
Notice: I use C# Codes to show a example.
Then you could send request as below:
Get webapp:
Url: https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/Microsoft.Web/sites?api-version={api-version}
Method: PUT
Parameter:
subscriptionId The identifier of your subscription where the snapshot is being created.
resourceGroup The name of the resource group that will contain the snapshot.
api-version The version of the API to use.
The codes is as below:
// Display the file contents to the console. Variable text is a string.
string tenantId = "xxxxxxxxxxxxxxxxxxxxx";
string clientId = "xxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
string subscriptionid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string resourcegroup = "BrandoSecondTest";
string version = "2015-08-01";
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientSecret);
var result = authenticationContext.AcquireTokenAsync(resource: "https://management.azure.com/", clientCredential: credential).Result;
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Web/sites?api-version={2}", subscriptionid, resourcegroup, version));
request.Method = "GET";
request.Headers["Authorization"] = "Bearer " + token;
request.ContentType = "application/json";
var httpResponse = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
Console.WriteLine(streamReader.ReadToEnd());
}
Console.ReadLine();
Create WebApp:
You should send your webapp template in the request content.
Then you could send request as below:
Url: https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/Microsoft.Web/sites/{appname}?api-version={api-version}
Method: PUT
Parameter:
subscriptionId The identifier of your subscription where the snapshot is being created.
resourceGroup The name of the resource group that will contain the snapshot.
appname: The name of your webapplicaiton you want to create
api-version The version of the API to use.
Request content:
{
"location": "East Asia",
"properties": {
"name": "YourWebpplcaitonName",
"serverFarmId": "Yourserviceplan"
}
}
Here is a C# example, hope it gives some tips:
string body = File.ReadAllText(#"D:\json.txt");
// Display the file contents to the console. Variable text is a string.
string tenantId = "xxxxxxxxxxxxxxxxxxxxxxxxx";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxx";
string subscriptionid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string resourcegroup = "BrandoSecondTest";
string appname = "Brandosss";
string version = "2015-08-01";
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientSecret);
var result = authenticationContext.AcquireTokenAsync(resource: "https://management.azure.com/", clientCredential: credential).Result;
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Web/sites/{2}?api-version={3}", subscriptionid, resourcegroup,appname, version));
request.Method = "PUT";
request.Headers["Authorization"] = "Bearer " + token;
request.ContentLength = body.Length;
request.ContentType = "application/json";
try
{
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(body);
streamWriter.Flush();
streamWriter.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// Get the response
var httpResponse = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
Console.WriteLine(streamReader.ReadToEnd());
}
I am getting an error "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature."
I followed the authorization tutorial provided by Microsoft, Delete Table, Authentication for the Azure Storage Services.
Am I missing anything?
It seems that you’d like to delete table via rest api.
DELETE https://myaccount.table.core.windows.net/Tables('mytable')
the following sample works fine on my side, please refer to the code to generate the signature.
string StorageAccount = "account name here";
string StorageKey = "account key here";
string tablename = "table name";
string requestMethod = "DELETE";
string mxdate = "";
string storageServiceVersion = "2015-12-11";
protected void Button1_Click(object sender, EventArgs e)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture,
"https://{0}.table.core.windows.net/Tables('{1}')",
StorageAccount, tablename));
req.Method = requestMethod;
//specify request header
string AuthorizationHeader = generateAuthorizationHeader();
req.Headers.Add("Authorization", AuthorizationHeader);
req.Headers.Add("x-ms-date", mxdate);
req.Headers.Add("x-ms-version", storageServiceVersion);
req.ContentType = "application/json";
req.Accept = "application/json;odata=minimalmetadata";
using (HttpWebResponse response = (HttpWebResponse)req.GetResponse())
{
}
}
public string generateAuthorizationHeader()
{
mxdate = DateTime.UtcNow.ToString("R");
string canonicalizedResource = $"/{StorageAccount}/Tables('{tablename}')";
string contentType = "application/json";
string stringToSign = $"{requestMethod}\n\n{contentType}\n{mxdate}\n{canonicalizedResource}";
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(StorageKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
String authorization = String.Format("{0} {1}:{2}",
"SharedKey",
StorageAccount,
signature
);
return authorization;
}
Working with the event hub and found something I find quite odd.
How can I send data to the eventhub acting as a device which i'm not.
private static Task<HttpResponseMessage> PostTelemetryAsync(string test)
{
var serviceNamespace = "dev-hub";
var hubName = "eventhub";
var url = string.Format("/{0}/publishers/testdevice/messages/", hubName);
// Create client.
var httpClient = new HttpClient
{
BaseAddress = new Uri(string.Format("https://{0}.servicebus.windows.net/", serviceNamespace))
};
var payload = JsonConvert.SerializeObject(test);
var sas = createToken("dev-hub", "anotherDevice", "IdmUSeHmcrLfjSfc2ssJVvLcsMIHM/uqG1xSLUIh5t4=");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", sas);
var content = new StringContent(payload, Encoding.UTF8, "application/json");
content.Headers.Add("ContentType", "application/json");
return httpClient.PostAsync(url, content);
}
private static string createToken(string resourceUri, string keyName, string key)
{
TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
var week = 60 * 60 * 24 * 7;
var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);
string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
return sasToken;
}
In the code above I´m generating a SAS token for the device anotherDevice but posting to the url ...publishers/testdevice/messages/. That is a different device.
The eventprocessor I´m using thinks that the data is sent from the testdevice but the SAS token is generated for anotherDevice.
Is it supposed to work like this? How can I use a SAS token for a different device to send data to the hub or am I missing something here?
The sas token it is to authorize your application to send data. You can have hundreads of different publishers all of them using the same sas token. Think as the authorization being this shared key that you distribute to your devices, you don't need to register the publishers before you send. Use the token just as a key to send metrics not as a way to register which device sent the data.