I'm developing a REST service that uses MS Azure Access Control Service for authentication. If the examples are any indication, the typical way to secure a REST service this way would be to provide a global username and pw, private key, or X.509 cert for the protected service. However, I want to use the passive user login mechanism on a mobile device with a flow more like the following:
Unauthenticated user attempts to access protected service from app
Mobile app redirects to browser app (or embedded browser)
User selects identity provider to use for login (facebook, google, etc.) from ACS login page
User enters credentials for identity provider
Browser redirects back to app
App somehow gets the SWT token to use with subsequent REST requests.
I'm stuck at about step 5--getting the SWT token, and the existing examples I've found don't seem to address this scenario. In addition, I'm actually trying to build a proof of concept with a desktop client in WPF, which may complicate things. Can anyone suggest a specific tutorial or a path to pursue that uses the per-user authentication vs. per-service? Thanks.
EDIT:
As I'm digging into this deeper, I've realized that the examples posted below (and most others) are based on OAuth WRAP, which has been deprecated in favor of OAuth 2.0. Can anyone suggest a more up to date reference? Googling has turned up http://blogs.msdn.com/b/adventurousidentity/archive/2011/09/18/acs-v2-oauth-2-0-delegation-support-explained.aspx and http://connect.microsoft.com/site1168/Downloads/DownloadDetails.aspx?DownloadID=32719 but they're not the most intuitive.
You should look into the ACS Windows Phone sample:
http://msdn.microsoft.com/en-us/library/gg983271.aspx
Here instead of using Silverlight you will be using WPF. Most of the code should be re-usable. Note that since you are using WPF you will need to register your own object for scripting e.g:
[ComVisibleAttribute(true)]
public class NotifyHandler
{
public void Notify(string notifyString)
{
// Here I have the token.
}
}
this.webBrowser1.ObjectForScripting = new NotifyHandler();
Update:
The sample above uses OAuth Wrap to contact the secured service. If you would like to use OAuth2 you should change the way the "Authorization" header set:
OAuth WRAP case:
WebClient client = new WebClient();
client.Headers["Authorization"] = "OAuth " + _rstrStore.SecurityToken;
OAuth2 case:
WebClient client = new WebClient();
client.Headers["Authorization"] = string.Format("OAuth2 access_token=\"{0}\"", token);
You can use the "Simple Service" sample as a guide to implement your token validation in your REST service:
http://msdn.microsoft.com/en-us/library/gg185911.aspx
Yet if you would like to implement a more complete sample you can look at how CustomerInformationService is protected in the CTP version 1.4:
https://connect.microsoft.com/site1168/Downloads/DownloadDetails.aspx?DownloadID=35417
Take a look at this one:
WPF Application With Live ID, Facebook, Google, Yahoo!, Open ID
http://social.technet.microsoft.com/wiki/contents/articles/4656.aspx
Related
I need to create an organizational feed to host nuget packages shared among projects on our Azure DevOps environment.
After several unsuccesful attempts and research, I discovered that the only way to create an organizational feed is, by design from Microsoft mouth, the Azure DevOps API.
Source for the claim : This question on VS dev community
and The MS docs on project-scoped feeds
Basically, I just need to be able to perform a POST here :
https://feeds.dev.azure.com/{organization}/_apis/packaging/feeds?api-version=5.1-preview.1
with the body :
{
"name": "{myfeedname}",
"hideDeletedPackageVersions": true,
"upstreamEnabled": true
}
And of course, a Bearer token to authenticate myself. That's the point where I'm confused.
What is the simplest way to obtain one ? I'm logged in through my company Microsoft AD account on my computer browser on Azure DevOps. I don't see any Bearer token that I can "steal" to use in PostMan in my browser dev tools.
The API docs described some relevant info, but I'm confused on how to use it in Postman :
Security oauth2
Type: oauth2
Flow: accessCode
Authorization URL: https://app.vssps.visualstudio.com/oauth2/authorize&response_type=Assertion
Token URL: https://app.vssps.visualstudio.com/oauth2/token?client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
Scopes Name Description
vso.packaging_write Grants the ability to
create and read feeds and packages.
Here is the interface in Postman for OAuth2:
Ican see how the info in the docs relates to the fields 1 - 2 - 3 - 4, but then, what callback url should I use ? What credentials ? my Microsoft email + password from AD ?
I tried this, and all I seem to get is this from Postman :
{"$id":"1","innerException":null,"message":"A potentially dangerous Request.Path value was detected from the client (&).","typeName":"System.Web.HttpException, System.Web","typeKey":"HttpException","errorCode":0,"eventId":0}
TLDR
How do I properly proceed to get a token with Postman, or other tool to manually execute my one-time request to Azure DevOps REST API ?
notes :
Following info here : Unable to get Authorization code for Devops using Postman oAuth2.0
, leading here : https://github.com/Microsoft/azure-devops-auth-samples/tree/master/OAuthWebSample , I understand that I have to register and run a whole web application. Am I understanding this correctly ? I there a simpler way ?
I understand that I have to register and run a whole web application. Am I understanding this correctly ? I there a simpler way ?
Yes, you are right. You have to register whole web application.
As the interface in Postman for OAuth2, we need provide the CallbackUrl, ClientID, ClientSecret and so on. Then, we check the document Requesting an OAuth 2.0 token, we could to know the Callback URL is:
The client application callback URL redirected to after auth, and that
should be registered with the API provider.
So, we have to register an OAuth client app in Azure DevOps (https://app.vsaex.visualstudio.com/app/register), then we could get the following information, like:
You could check the document Authorize access to VSTS REST APIs with OAuth 2.0 for some more details.
AFAIK, there is currently no simpler way to get a bearer token to send requests to the Azure DevOps API.
Hope this helps.
I have a functions app in azure that both a machine (client credential flow) and humans (authorization code flow) need to be able to authorize/authenticate against.
Initially I was using easy-auth, Azures out-of-the-box solution for securing functions apps. However according to this https://stackoverflow.com/a/57357226/7411328 it's not possible to use the client credentials flow with easy auth. Although I don't understand why this is. Why is it not possbile to use the same authority for two different flows with a single app registration?
Making the assumption (perhaps incorrectly) that the above is true and I have to implement JWT validation on my own.
Is there any reliable way to tell whether an API is being called by a machine or by a human?
Should I still do it with two seperate app registrations?
My understanding of these technologies might inadequate to properly ask the question, please let me know if I can do anything to clarify the question.
As far as I know, you can use client credentials flow to call an Azure function that protected by easy-auth(AAD as auth provider).Generally ,you can try the steps below :
Register an Azure AD App
Getting an access token from Azure AD by request below :
URL:
POST https://login.microsoftonline.com/<your tenant ID/name>/oauth2/token
Header:
Content-Type: application/x-www-form-urlencoded
Body:
client_id=<your new resistered app ID>&
client_secret=<your new resistered app secret>&
resource=<your Azure function app ID which configed at easy-auth>&
grant_type=client_credentials
Result:
Use this access token to call Azure function :
If you are using Azure AD b2c , pls provide me with more detailed infos , and I'll do some research for you .
I'm trying to get my head around setting up an OpenID Connect server for SSO authentication. I think my basic setup/requirements are pretty standard, but I'm having a little difficulty putting it all together.
The broad setup is a single page application, a web API, and an identity server. The SPA is served from the same domain name as the web API and the ID server is on a different domain, so I might have several SPA/Web API combinations, but of course every case is the same setup (single host with static content and an API). At the moment I'm working with IdentityServer4 to create the identity server; I'm flexible to trying other providers if there's some kind of problem with that one, but so far so good.
My login requirements are also pretty standard I think; I want to have short-lived access tokens and I also want to use refresh tokens to implement a sliding expiration so users don't have to be redirected off of my SPA until they've been inactive for "a while" (however I end up defining that).
After a bit of research, I think what I want is to use the authorization code flow. So generally, the way I thought this would work is:
A user visits the application host (that serves the web API and SPA); the static SPA is served
The SPA loads and determines that there is no access token in local storage. The SPA kicks off the login process by producing a random identifier and storing it in session storage, then navigates the browser to the ID server host
The user authenticates with the ID server host
The ID server hosts redirects to the client and includes in the redirect the random identifier the SPA originally generated along with an authorization code
Upon loading and detecting that it got an access code, the SPA checks session storage for the identifier stored in step 2. Finding it, the SPA calls the web API to exchange the authorization code for an access token
The web API uses a back channel with the ID server to produce an access token and refresh token
The web API stores the refresh token and access token then issues the access token to the client
In all future requests, the client uses the access token with the Web API. When the SPA determines that the access token it has is expired or about to expire, it request a refresh somehow (I'm going to hand-wave the refresh a bit for now)
So I went through the tutorial on the IdentityServer4 site, and to my surprise I ended up in a bit of a different state. It took me a while to work through it; the step I'm talking about if anyone wants to follow along is "Adding a JavaScript Client", but I'd be willing to be the result is common among people implementing OpenID Connect. The resulting flow differed from what I expected starting with step 5; instead of the SPA calling the web API with an authorization code and requesting an access token, the SPA uses CORS and makes a cross-domain request back to the ID server to request the access token. The tutorial didn't really cover refresh tokens all that much (there's other parts of the docs that do, but only briefly), but I think the implication is that if I wanted to use refresh tokens they'd be issued to the client and it would use local storage to store them; then for future refreshes it'd also do a cross-domain request back to the ID server. As a side note, another bit of surprise was that the tutorial has you use PKCE, which on research seems to be unnecessary for a web application; it's somewhat important as including a SHA-2 implementation client-side increases the size of my application by a fair bit.
I believe it is a bad practice to issue a refresh token to a web client and ask it to store it; I'm somewhat vague on the specific vulnerabilities that opens up, but the general idea is that if someone subverts your client somehow, a refresh token is considerably more powerful than a short-lived access token.
So, getting my head around this, I believe the way I originally though this would work was that the web API is the "Relying party" in OAuth 2 parlance, and the tutorial set it up so that the client is the "Relying party". It makes me think that if I want to get a sliding expiration, I have to go past where the tutorial went and move the functionality for token exchange from the client into the web API like I had originally envisioned. It would end up looking a bit like the web API functionally being a proxy for the SPA to exchange the authorization code for an access token.
Ultimately, my question is: am I getting this right? It looks like there are really two different models for implementing OpenID Connect for SPA/API web applications; one where the API is the RP, and another where the SPA is the RP. If you want to use refresh tokens, I think you should go with option 1, but maybe if you care that the API could impersonate the client you'd go with option 2? That still seems like it wouldn't matter to me; that authorization code/access token swap can only be used for a particular application, so it's not like one API could suddenly authenticate as a different backend in that setup. I'm just nervous about going off on my own to structurally alter the setup the tutorial had since this is security-related.
UPDATE
I used the authorization code flow instead of the implicit flow despite the accepted answer, since that's the most recent recommendation of the IETF (see https://datatracker.ietf.org/doc/html/draft-parecki-oauth-browser-based-apps-02#section-4, and a great writeup at https://brockallen.com/2019/01/03/the-state-of-the-implicit-flow-in-oauth2/). I accepted that answer because using a silent refresh via iframe instead of a refresh token seems to be the most standard approach for what I'm trying to do; using that I was able to build a working system that looks like the tutorial. In fact, the client library it recommends (oidc-client) has a built-in function to handle the details. For completeness, what I'm starting off with is this service:
import oidc from "oidc-client";
import Url from "url-parse";
let baseUrl = new Url(window.location.href).set("pathname", "").set("query", "").set("hash", "");
let redirectUrl = (new Url(baseUrl)).set("query", "redirect=fromIdentityProvider");
let silentRedirectUrl = (new Url(baseUrl)).set("pathname", "silent-refresh.html");
let identitySettings = {
authority: "[my application's id server domain]",
client_id: "[my client's id]",
redirect_uri: redirectUrl.toString(),
response_type: "code",
scope: "openid profile [my application's resource name]",
post_logout_redirect_uri: baseUrl,
automaticSilentRenew: true,
silent_redirect_uri: silentRedirectUrl.toString()
};
let userManager = new oidc.UserManager(identitySettings);
let user = null;
export default {
async logIn() {
await userManager.signinRedirect();
},
async isLoggedIn() {
return !!(await this.getAccessToken());
},
async logOut() {
await userManager.signoutRedirect();
},
async getAccessToken() {
user = await userManager.getUser();
return user ? user.access_token : null;
},
async initializeApp() {
let url = new Url(window.location.href, true);
if (url.query && url.query.redirect === "fromIdentityProvider") {
await new oidc.UserManager({
response_mode: "query"
}).signinRedirectCallback();
window.location = "/";
return false;
}
user = await userManager.getUser();
return true;
}
};
Then in my application I call initializeApp when the app starts and getAccessToken before any API calls. I still need to eventually add the ability to automatically redirect on 401 from the API, but that's pretty easy.
To make the silent redirect work, I created silent-redirect.html based on instructions here: https://www.scottbrady91.com/OpenID-Connect/Silent-Refresh-Refreshing-Access-Tokens-when-using-the-Implicit-Flow. I also integrated Google authentication as an external provider and verified that it also works for silent refreshes, so no trade-off there.
To round it out, for me the answer to my original question is basically "no", I don't want to move the exchange step to the backend. I did also decide to use PKCE even though it seems to me like it shouldn't be necessary, it's in the IETF recommendation I mentioned, so I'll stick with that.
There is a special OAuth2 flow for SPAs - the Implicit grant. If you want just an access token, specify &response_type=token when accessing the /auth endpoint. Alternatively, you can ask for an ID token as well with &response_type=token id_token&scope=openid. The SPA gets the token in the redirect URL from the autorization provider (in the hash part #access_token=...) along with its life-time expires_in=.... So the token stays in your browser - the hash part doesn't get sent to the server hosting the SPA files.
Your SPA should validate and keep both values and before the token expiration, it should call the /auth endpoint in an iframe with &prompt=none parameter. If your authorization provider supports Single Sign On (SSO), then you should get a fresh access token without the user noticing it. So it works similarly to a refresh token, without requiring CORS, PKCE or a client secret.
If you wanted to implement some more sophisticated SSO management, take a look at the OpenID Connect Session management RFC.
I have an identity server built with Identity server 4. There is one main API with several angular web applications build by third party customers that access this API endpoint.
Now I would like to create a second API but its only for internal use with OUR official plugin. I am trying to figure out how to lock it down so that only our app can access it. I am not a fan security by obscurity and assuming that the third party's dont know its there so wont try and access it.
My first thought was to add a new scope for this API but by doing that its going to popup and ask the users for access to the data which isnt really need.
The only thing i can think of would be to check the client id some how in the API and add a policy for it. This really isnt right ether as to my understanding polciy should be checking stats of the user and not the client itself.
services.AddAuthorization(
options => {
options.AddPolicy("DevConsole", policy => IsClientId(xxxx)
}
);
Is it possible to lock down an API based upon a single client id? or am i going at this in the wrong way.
Another idea i had was to add another claim if they login with this client id which seams like overkill to me.
Example:
Lets say that I have an API endpoint that allows you to update the usersname. All users have access to their name this isnt a scope issue. However only our official app has access to update the usersname. Any app created by third party developers do not have access to the endpoint to update a usersname.
So our official plugin has a client id of 123 and yours has a client id of 321. A user logged though client id 321 can not access this endpoint. User logged in though client id 123 can.
I am starting to think this isnt possible because Oauth and Openid are completely user based. There is no way to validate the user based upon the client they authenticated with.
if I understand the problem correctly, I would create a new Client on Identity Server, for the "main API" and a new Resource for the "internal API"
This would allow the "main API" to also be a client, with client credentials grant type, therefore it has a id+secret and is allowed to request a token for itself. In this case, you will now request the newly created scope for in "internal API" and the users will have no knowledge that this entity evens exists.
After going back and forth with this with this it occurred to me that the client id is returned as a claim. So when i got in this morning i checked.
This should enable me to add a policy for only our official plugin.
services.AddAuthorization(options =>
{
options.AddPolicy("IsOfficalApp", policy => IsCheckOfficalClient());
});
This should enable me to lock down the API endpoints in question without requiring additional authorization from the users.
Secure 2 different web applications with one identify service
Identity Service: Thinktecture Identity Service V2
Application 1: Asp.net MVC 5 application
Application 2: Asp.net Web API application
The above Applcation 1 and Application 2 are different projects and hosted in different servers. Now my scenarios are
1. Want to secure Application 1 with Identity Service.
2. Want to secure Application 2 with Identity Service.
Use case 1: If user access Application 1 it should redirect to identity service login page, once I entered the credentials and login in to application 1, the same token allow me to access Application 2.
Use case 2: If user try to access any API from Application 2 without login into Identity service, the request should reject.
I found a way to do this using pure SAML tokens. The trick is you need to create a delegation account in Identity Server to allow your web app to delegate identity to a specific realm (where your service lives). Then in the web app you make a service call using the the token that the user already has to get a new token which you use to access your service.
I asked a very similar question and answered it myself here.
Ok. I did eactly the same thing just now. Everything required to get that done is written here. If you're using IdentityServer, you need to configure the Token Type of your RP to be JWT:
this allows you to later extract the token from your authenticated MVC 5 application (see the link above to see how to do this) and then send that token to your Web API. You then need to tell you web api to accept that token, using Microsoft's JwtSecurityTokenHandler class. This class has a ValidateToken() method which accepts 2 parameters, the first being the access token that you put into your auth headers of the requests to the Web API, and the second, the validation parameters are basically what you've defined in IdentityServer's config:
validationParams = new TokenValidationParameters
{
AllowedAudiences = _allowedAudiencesAndSigningKeys.Select(x => x.Key),
ValidIssuer = ConfigurationManager.AppSettings["IssuerIdentity"],
ValidateIssuer = true,
SigningTokens = _allowedAudiencesAndSigningKeys.Select(x => new BinarySecretSecurityToken(Convert.FromBase64String(x.Value)))
};
The Audience(s)/Realm(s) you want to allow access to, the issuer name (your Identity Server name) and the signing symmetric key(s) of the applications you have defined in Identity Server and want to grand access to. The ValidateToken() method returns an ClaimsPrincipal with a list of the claims extracted from the token. The code to do all this can be put in a message handler:
public static void Configure(HttpConfiguration config)
{
var authNConfig = new AuthenticationConfiguration();
config.MessageHandlers.Add(new MyTokenValidationHandler());
}