Given I have the following info from Azure app registration:
Application (client) ID,
Client secret,
Directory (tenant) ID,
Object ID
Is there a way to check it's a valid credential programmatically (like using curl etc but not powershell)?
If you meant to check client secret validity or even the properties of that app ,then please check if the below c# code can be worked around .We can try to query the application and see expiry date of secret. Please grant the app with Directory.Read.All ,Application.Read.All permission to this API for using client credentials flow.
var graphResourceId = "https://graph.microsoft.com";
var applicationId= "";
var ObjectId = "";
var clientsecret = "";
var clientCredential = new ClientCredential(applicationId,secret);
var tenantId = "xxx.onmicrosoft.com";
AuthenticationContext authContext = new AuthenticationContext($"https://login.microsoftonline.com/{tenantId}");
//get accesstoken
var accessToken = authContext.AcquireTokenAsync(graphResourceId, clientCredential).Result.AccessToken;
Uri servicePointUri = new Uri(graphResourceId);
Uri serviceRoot = new Uri(servicePointUri, tenantId);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await Task.FromResult(accessToken));
var app = activeDirectoryClient.Applications.GetByObjectId(appObjectId).ExecuteAsync().Result;
foreach (var passwordCredential in app.PasswordCredentials)
{
Console.WriteLine($"KeyID:{passwordCredential.KeyId}\r\nEndDate:{passwordCredential.EndDate}\r\n");
}
If you want , you can even request token using curl this way and validate using post man or by checking token in https://jwt.io .
Reference: check client secret expiry using C#
Related
I'm trying to create a local Java-based client that interacts with the SurveyMonkey API.
SurveyMonkey requires a long-lived access token using OAuth 2.0, which I'm not very familiar with.
I've been googling this for hours, and I think the answer is no, but I just want to be sure:
Is it possible for me to write a simple Java client that interacts with the SurveyMonkey, without setting up my own redirect server in some cloud?
I feel like having my own online service is mandatory to be able to receive the bearer tokens generated by OAuth 2.0. Is it possible that I can't have SurveyMonkey send bearer tokens directly to my client?
And if I were to set up my own custom Servlet somewhere, and use it as a redirect_uri, then the correct flow would be as follows:
Java-client request bearer token from SurveyMonkey, with
redirect_uri being my own custom servlet URL.
SurveyMonkey sends token to my custom servlet URL.
Java-client polls custom servlet URL until a token is available?
Is this correct?
Yes, it is possible to use OAuth2 without a callback URL.
The RFC6749 introduces several flows. The Implicit and Authorization Code grant types require a redirect URI. However the Resource Owner Password Credentials grant type does not.
Since RFC6749, other specifications have been issued that do not require any redirect URI:
RFC7522: Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants
RFC7523: JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
RFC8628: OAuth 2.0 Device Authorization Grant
In any case, if the grant types above do not fit on your needs, nothing prevent you from creating a custom grant type.
Not exactly, the whole point of the OAuth flow is that the user (the client you're accessing the data on behalf of) needs to give you permission to access their data.
See the authentication instructions. You need to send the user to the OAuth authorize page:
https://api.surveymonkey.net/oauth/authorize?api_key<your_key>&client_id=<your_client_id>&response_type=code&redirect_uri=<your_redirect_uri>
This will show a page to the user telling them which parts of their account you are requesting access to (ex. see their surveys, see their responses, etc). Once the user approves that by clicking "Authorize" on that page, SurveyMonkey will automatically go to whatever you set as your redirect URI (make sure the one from the url above matches with what you set in the settings for your app) with the code.
So if your redirect URL was https://example.com/surveymonkey/oauth, SurveyMonkey will redirect the user to that URL with a code:
https://example.com/surveymonkey/oauth?code=<auth_code>
You need to take that code and then exchange it for an access token by doing a POST request to https://api.surveymonkey.net/oauth/token?api_key=<your_api_key> with the following post params:
client_secret=<your_secret>
code=<auth_code_you_just_got>
redirect_uri=<same_redirect_uri_as_before>
grant_type=authorization_code
This will return an access token, you can then use that access token to access data on the user's account. You don't give the access token to the user it's for you to use to access the user's account. No need for polling or anything.
If you're just accessing your own account, you can use the access token provided in the settings page of your app. Otherwise there's no way to get an access token for a user without setting up your own redirect server (unless all the users are in the same group as you, i.e. multiple users under the same account; but I won't get into that). SurveyMonkey needs a place to send you the code once the user authorizes, you can't just request one.
You do need to implement something that will act as the redirect_uri, which does not necessarily need to be hosted somewhere else than your client (as you say, in some cloud).
I am not very familiar with Java and Servelets, but if I assume correctly, it would be something that could handle http://localhost:some_port. In that case, the flow that you describe is correct.
I implemented the same flow successfully in C#. Here is the class that implements that flow. I hope it helps.
class OAuth2Negotiator
{
private HttpListener _listener = null;
private string _accessToken = null;
private string _errorResult = null;
private string _apiKey = null;
private string _clientSecret = null;
private string _redirectUri = null;
public OAuth2Negotiator(string apiKey, string address, string clientSecret)
{
_apiKey = apiKey;
_redirectUri = address.TrimEnd('/');
_clientSecret = clientSecret;
_listener = new HttpListener();
_listener.Prefixes.Add(address + "/");
_listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
}
public string GetToken()
{
var url = string.Format(#"https://api.surveymonkey.net/oauth/authorize?redirect_uri={0}&client_id=sm_sunsoftdemo&response_type=code&api_key=svtx8maxmjmqavpavdd5sg5p",
HttpUtility.UrlEncode(#"http://localhost:60403"));
System.Diagnostics.Process.Start(url);
_listener.Start();
AsyncContext.Run(() => ListenLoop(_listener));
_listener.Stop();
if (!string.IsNullOrEmpty(_errorResult))
throw new Exception(_errorResult);
return _accessToken;
}
private async void ListenLoop(HttpListener listener)
{
while (true)
{
var context = await listener.GetContextAsync();
var query = context.Request.QueryString;
if (context.Request.Url.ToString().EndsWith("favicon.ico"))
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.Close();
}
else if (query != null && query.Count > 0)
{
if (!string.IsNullOrEmpty(query["code"]))
{
_accessToken = await SendCodeAsync(query["code"]);
break;
}
else if (!string.IsNullOrEmpty(query["error"]))
{
_errorResult = string.Format("{0}: {1}", query["error"], query["error_description"]);
break;
}
}
}
}
private async Task<string> SendCodeAsync(string code)
{
var GrantType = "authorization_code";
//client_secret, code, redirect_uri and grant_type. The grant type must be set to “authorization_code”
var client = new HttpClient();
client.BaseAddress = new Uri("https://api.surveymonkey.net");
var request = new HttpRequestMessage(HttpMethod.Post, string.Format("/oauth/token?api_key={0}", _apiKey));
var formData = new List<KeyValuePair<string, string>>();
formData.Add(new KeyValuePair<string, string>("client_secret", _clientSecret));
formData.Add(new KeyValuePair<string, string>("code", code));
formData.Add(new KeyValuePair<string, string>("redirect_uri", _redirectUri));
formData.Add(new KeyValuePair<string, string>("grant_type", GrantType));
formData.Add(new KeyValuePair<string, string>("client_id", "sm_sunsoftdemo"));
request.Content = new FormUrlEncodedContent(formData);
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
_errorResult = string.Format("Status {0}: {1}", response.StatusCode.ToString(), response.ReasonPhrase.ToString());
return null;
}
var data = await response.Content.ReadAsStringAsync();
if (data == null)
return null;
Dictionary<string, string> tokenInfo = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
return(tokenInfo["access_token"]);
}
}
Is it possible to update a User Claim during a active SAML Session without a complete relogging to the application?
I want to change a specific claim (activeSite) that we use for authorization in backend and for filtering in queries.
I'm working with an .Net Core API and a Angular/Ionic frontend.
The code we use is from the TestWebAppCoreAngularApi Project with a little adoption by setting some custom user claims.
[Route("AssertionConsumerService")]
public async Task<IActionResult> AssertionConsumerService()
{
var binding = new Saml2PostBinding();
var saml2AuthnResponse = new Saml2AuthnResponse(config);
binding.ReadSamlResponse(Request.ToGenericHttpRequest(), saml2AuthnResponse);
if (saml2AuthnResponse.Status != Saml2StatusCodes.Success)
{
throw new AuthenticationException($"SAML Response status: {saml2AuthnResponse.Status}");
}
binding.Unbind(Request.ToGenericHttpRequest(), saml2AuthnResponse);
var relayStateQuery = binding.GetRelayStateQuery();
var activeSite = relayStateQuery.ContainsKey(relayStateActiveSite) ? relayStateQuery[relayStateActiveSite] : Url.Content("-1");
AddCustomClaims(saml2AuthnResponse, Int32.Parse(activeSite));
await saml2AuthnResponse.CreateSession(HttpContext, claimsTransform: (claimsPrincipal) => ClaimsTransform.Transform(claimsPrincipal));
var returnUrl = relayStateQuery.ContainsKey(relayStateReturnUrl) ? relayStateQuery[relayStateReturnUrl] : Url.Content("~/");
return Redirect(returnUrl);
}
Is it even possible to update that active claim (activeSite) during a active session?
And if so how can I update the claim in .Net?
I tryed something like removing and adding the claim with new value but this seems not to work correctly.
var principle = (ClaimsIdentity)User.Identity;
principle.RemoveClaim(claim);
principle.AddClaim(claim);
It is NOT possible to change claims after a successfully authentication flow. Changing claims require a re-login.
Hovewer, it is possible to change claims after user login in the authentication flow, by calling the ClaimsTransform.Transform, and the ClaimsTransform class.
My client has synchronization set up between an on-premises Active Directory (AD) and Azure Active Directory (AAD).
I am able to retrieve user information from AAD using Microsoft Graph without a problem but, I specifically need to get the AD UserID, ie ({domain}/{userid}).
I tried calling https://graph.microsoft.com/v1.0/users/firstname.lastname#domain.com/$select=userId but it did not work.
My questions are, is it possible? And in that case what is the actual attribute name? I have been looking around but haven´t been able to find a complete list of attributes.
EDIT:
After receiving one answer from Marilee I am including the C# code I have been using, ish. Both the calls do work for receiving user information from AAD, but not the AD UserID, ie ({domain}/{userid}) that I am looking for.
Attempt no 1
var requestUri = GraphBaseUri + $"/v1.0/users/{upn}?$select=userId";
var response = await _httpClient.GetAsync(new AuthenticationHeaderValue("Bearer", accessToken), requestUri).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
dynamic responseObj = JsonConvert.DeserializeObject(content) as JObject;
return responseObj.UserId; //NOT WORKING
Attempt no 2
var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) => {
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return Task.FromResult(0);
}));
// Retrieve a user by userPrincipalName
var user = await graphClient
.Users[upn]
.Request()
.GetAsync();
return user.ObjectId; //NOT WORKING
The attribute you're referring to is the objectID. From Graph API you can use UPN like you said:
GET /users/{id | userPrincipalName}
You can look up the user in a few different ways. From the /users endpoint you can either use their id (the GUID assigned to each account) or their userPrincipalName (their email alias for the default domain):
// Retrieve a user by id
var user = await graphClient
.Users["00000000-0000-0000-0000-000000000000"]
.Request()
.GetAsync();
// Retrieve a user by userPrincipalName
var user = await graphClient
.Users["user#tenant.onmicrosoft.com"]
.Request()
.GetAsync();
If you're using either the Authorization Code or Implicit OAuth grants, you can also look up the user who authenticated via the /me endpoint:
var user = await graphClient
.Me
.Request()
.GetAsync();
From Powershell you can query Object IDs:
$(Get-AzureADUser -Filter "UserPrincipalName eq 'myuser#consoso.com'").ObjectId
The way to do this is;
var requestUri = GraphBaseUri + $"/v1.0/users/{upn}?$select=onPremisesSamAccountName";
var response = await _httpClient.GetAsync(new AuthenticationHeaderValue("Bearer", accessToken), requestUri).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var responseObj = JsonConvert.DeserializeObject<ResponseObj>(content);
return responseObj.OnPremisesSamAccountName;
Notice the select where we collect the onPremisesSamAccountName. Though, I haven´t found a comprehensive list of all attributes possible to retrieve which would have been nice.
Requirement - I am trying to connect to azure SQL DB from a asp.net MVC application and the connection type to azure SQL DB is "token based" and below are the set up done from my end.
a. Created an AAD application( ex : MTSLocal ) with certificate based authentication.
b. Added permission to the above AAD in SQL.
CREATE USER [MTSLocal] FROM external provider;
c.In code level I am trying to get a access token by using Client ID( obtained from step a.) and certificate and the resource I am connecting to is "https://database.windows.net". Please refer the sample code -
string authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, "https://login.windows.net/{0}",
"xxxx.onmicrosoft.com");
var authContext = new AuthenticationContext(authority);
AuthenticationResult result = null;
result = await authContext.AcquireTokenAsync("https://database.windows.net", AssertionCert);
token = result.AccessToken;
d. I am able to retrieve the access token but when I am trying to open the SQL connection.I am getting the above said error.
sqlBuilder["Data Source"] = serverName;
sqlBuilder["Initial Catalog"] = databaseName;
sqlBuilder["Connect Timeout"] = 30;
string accesstoken = GetAccessToken();
using (SqlConnection connection = new SqlConnection(sqlBuilder.ConnectionString))
{
try
{
connection.AccessToken = accesstoken;
connection.Open();
}
catch (Exception ex)
{
}
}
Any help on this would be really helpful.
Here is some rough and ready code on how I solved this. I had to supply the host tenant (see in the code below.
private async Task<string> SqlServerVersion()
{
var provider = new AzureServiceTokenProvider();
var token = await provider.GetAccessTokenAsync("https://database.windows.net/", "<host tenant>.onmicrosoft.com").ConfigureAwait(false);
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder
{
csb.DataSource = "<your server>.database.windows.net";
csb.InitialCatalog = "<your database>";
};
using (var conn = new SqlConnection(csb.ConnectionString))
{
conn.AccessToken = token;
await conn.OpenAsync().ConfigureAwait(false);
using (var sqlCommand = new SqlCommand("SELECT ##VERSION", conn))
{
var result = await sqlCommand.ExecuteScalarAsync().ConfigureAwait(false);
return result.ToString();
}
}
}
The Application Registered in the AAD should be added to the users list of the DB and respective roles should be given to DB USER.
For suppose the name of the App registered is "App_AAD_Register_Name". add this user to the corresponding DB like executing the below query. With this the user will be added to Principal Users list of the DB server.
CREATE USER [App_AAD_Register_Name] FROM EXTERNAL PROVIDER.
Create some generic Role like below
CREATE ROLE [RoleUser]
GO
GRANT SELECT ON SCHEMA :: dbo TO [RoleUser]
GO
GRANT INSERT ON SCHEMA :: dbo TO [RoleUser]
GO
Once Role is created and respective permissions are given, assign the role to the user created in the first step.
EXEC sp_addrolemember N'RoleUser', N'App_AAD_Register_Name'.
Once all these steps are done you will be able to connect to DB with the token.
These steps worked for me.
I'm currently writing an angular application that first authenticates against think texture identityserver3.
This works fine, and I receive the bearer token without any issues.
When I use my token on an call to my API, I'm authenticated. I can see my userid, but have lost my claims (username, roles,...).
What do I have to do for transferring my claims with my token, or getting the roles from the identityserver?
You can tell Identity Server to include specific claims in an access token by adding that claim to your API's Scope.
Example:
var apiScope = new Scope {
Name = "myApi",
DisplayName = "My API",
Type = ScopeType.Resource,
Claims = new List<ScopeClaim> {
new ScopeClaim("myClaimType")
}
};
You can also use the AlwaysIncludeInIdToken property of ScopeClaim to include the claims in identity tokens as well as access tokens.
See https://identityserver.github.io/Documentation/docsv2/configuration/scopesAndClaims.html for more info.
We are doing something very similar using MS Web API 2 and a Thinktecture Identity Server v3.
To verify the user's claims we created an Authentication Filter, and then called the Identity server directly to get the user's claims. The bearer token only grants authentication and it is up to the API to get the claims separately.
protected override bool IsAuthorized(HttpActionContext actionContext)
{
string identityServerUrl = WebConfigurationManager.AppSettings.Get("IdentityServerUrl") + "/connect/userinfo";
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = actionContext.Request.Headers.Authorization;
var response = httpClient.GetAsync(identityServerUrl).Result;
if (response.IsSuccessStatusCode)
{
string responseString = response.Content.ReadAsStringAsync().Result;
Dictionary<string, string> claims = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseString.ToLower());
... Do stuff with your claims here ...
}
}
}