I am stuck with one very basic requirement in my application. Here is my scenario. I am building a custom e-commerce portal using angular as a front end and rest API as a back end. I have a product listing API that is going to be called by my angular client application without the user's credentials as the product listing is a public page. However, I don't want anyone else to consume my product listing API. I know I can use client id and client secret to obtain token and make my API secure using Identityserver 4 but, how do I avoid exposing my client secret in an angular app?
Anyone can steal it very easily. Is there any way to use Authorization Code flow with PKCE for public my APIs such as product listing API where user id and password are not required?
To me best approach to answer this is to go back to definition of Authorization Code flow.Authorization code grant is a redirection-based flow used to obtain both access tokens and refresh tokens, the client initiates the flow by directing the resource owner's.
And pkce is just is an extension to the Authorization Code flow to prevent certain attacks.
Then the answer is no, we can not use Authorization Code flow with PKCE to secure a public API without user login.
There is some other ways to secure public APIs or at least make it less/harder accessible. CORS is an option, read more here, as its out of context for this question I wouldn't go further.
For scenario with login, Authorization code flow is supported on IdentityServer4. here is what we need to do to implement it:
On IdentityServer add the configuration entry for js client like:
new Client
{
ClientId = "jsclient",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
RedirectUris = { "http://localhost:5003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
AllowedCorsOrigins =
{
"http://localhost:5003"
},
AllowedScopes = {"openid", "profile", "offline_access", "api1", "api2" },
}
On SPA app use oidc-client library, and also add the OIDC config to be like
var config = {
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:5003/callback.html",
response_type: "code",
scope:"openid profile api1",
post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);
Find full sample code here. I strongly suggest to read the quickstart doc to get a better understanding of implementation details.
You should implement Authorization Code Flow (PKCE) in your Angular App by plugging in the widely used OIDC Client library - its UserManager class will do the work for you.
For an example of how to use it, see these resources of mine:
Visual Tutorial Write Up
Code Sample
Authorization Code Flow PKCE Code
Related
I'm working with ASP.Net MVC and I have a problem using MSAL while authenticating a User. This is because, as we use AntiforgeryToken, when the user sign in in the page of Microsoft, the token breaks and we get an error related to the token.
My question is, is there a way to keep the token even after being redirected from Microsoft login page? Or can I recreate it?
I've search on other questions and google and found nothing.
Thank you.
Yes you can save the token in your application like this:
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Acquire the access token.
string[] scopes = new string[]{"user.read"};
string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
context.Token = accessToken;
}
Alternatively, you can explicitly acquire tokens by using the acquire-token methods as described in the core MSAL library. The MSAL wrapper provides the HTTP interceptor, which will automatically acquire access tokens silently and attach them to the HTTP requests to APIs.
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 been asked to create a 'Authentication/Authorization' Middle man or broker as an http,MVC web application, so that this can be used to multiple applications on our organization for authentication/Authorization purposes. Means, users will signup, Login on this broker application and once confirmed Authenticated, authorized user, he will get redirected to client applications accordingly. This is the use case.
I am choosing OAuth and OWIN to develop this broker in an MVC applicaiton, which means OAuth(Authorization) will issue access token + refresh token, once user is successfully authenticated. I use normal, simple, minimal authentication logic inside the Oauth Authorization Server's Login Controller as below :
public class AccountController : Controller
{
public ActionResult Login()
{
var authentication = HttpContext.GetOwinContext().Authentication;
if (Request.HttpMethod == "POST")
{
var isPersistent = !string.IsNullOrEmpty(Request.Form.Get("isPersistent"));
if (!string.IsNullOrEmpty(Request.Form.Get("submit.Signin")))
{
var user = Constants.Users.UserCollection.Where(u => u.Email.ToLower() == Request.Form["username"].ToLower().Trim() && u.Password == Request.Form["password"].Trim());
if (user.Count() > 0)
{
authentication.SignIn(
new AuthenticationProperties { IsPersistent = isPersistent },
new ClaimsIdentity(new[]
{ new Claim(ClaimsIdentity.DefaultNameClaimType, Request.Form["username"]),
new Claim("DisplayName", user.FirstOrDefault().DisplayName) } , "Application"));
}
}
}
return View();
}
This is the MSFT sample application I am following to develop this conceptual application.
https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server.
My question here is : I read in many articles like, its not good to use Oauth authentication, else use OPENID Connect handling authentication. To be frank, I am not used with OPENID Connect and I am not sure about the necessity of creating a OPENID Provider for my organization, Since this service will be used only by customers of our organization - less than 200,000 users. We hardly need a user signup and login, this account need to be used among different web applications of our organization. Please help me here with your inputs. Thanks in advance.
I think your question is about the benefits of OpenID Connect (OIDC) over OAuth 2.0.
OIDC builds upon OAuth 2.0 so you can use all of it's features. In a practical context, the question you should ask yourself is: Do other applications (clients, APIs), which use your "broker" (authorization server/security token service/OpenID provider) need to know something about the user, who just logged in? Do they need the ID, it's roles, username etc..? If the answer is no and you just need a signed token you are probably better of with OAuth.
If you start to include user claims (=attributes) in your access token you should at least have a look at OIDC. Also note, that even if you include claims in your access token, these are meant for the resource server (=API) and are normaly inaccessable for the client (unless you extract them and expose them on the API side - this is basically what the OIDC userinfo endpoint does).
I've implemented OAuth2 client_credentials flow in our MVC app. Our MVC app is actually the Resource in this scenario. I had much difficulty in securing a sample for this specific use case, since this flow is primarily used for API access, but I did it nonetheless.
I'd like to share some of the implementation details with you to ask for any information regarding vulnerabilities that I may be unaware of. I am in no way a security expert, which is what brought me here.
In .NET Framework 4.5.2 I used the Microsoft.Owin libraries v3.0.1. I know there are newer ways to set up this sort of thing, (.NET Core and IdentityServer4 for example), but as I said I was having difficulty finding a viable sample for this specific use case, so I did the best I could.
I implemented a Provider:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private ClientService clientService;
public ApplicationOAuthProvider()
{
this.clientService = new ClientService();
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
context.TryGetFormCredentials(out clientId, out clientSecret);
if (clientId == "XXXX" && clientSecret == "XXXXX")
{
context.Validated(clientId);
}
return base.ValidateClientAuthentication(context);
}
public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var client = clientService.GetClient(context.ClientId);
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket);
//context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
return base.GrantClientCredentials(context);
}
with the following startup code:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
static Startup()
{
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
//AllowInsecureHttp = true,
AuthenticationMode = AuthenticationMode.Active,
};
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll)
.UseOAuthBearerTokens(OAuthOptions);
//app.UseOAuthBearerTokens(OAuthOptions);
}
}
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
ConfigureAuth(app);
}
}
and then created a Client app that is also a website and eventually gained access to the Resource (MVC app). The Resource has no users, therefore no login screen. The Resource (now) has a token endpoint. The Client app makes a request to the token endpoint with their credentials and, (after being authenticated), then uses that token in subsequent requests to the Resource.
I've found 2 different ways of using this token to gain access.
Include the access token in the request header. OR
Include the access token as a form parameter
My questions are concerning vulnerabilities to this scenario:
Assuming all communications between the client app and the server are happening over secure channels (https) and the client app is able to maintain the credentials in a secure fashion, what are the chances that an access token could be obtained or intercepted? Or, are there methods included in this flow (or perhaps another OAuth flow) that also include client verification? I realize that the client is already being authenticated via client_id/client_secret, but when I ask about verification, I'm asking about the origin of the request (assuming of course, that the method of verification does not include checking something that can be spoofed by a malicious user).
Is there an additional step of verification that I should be including that I may have missed - because there's alot of information out there and I did my best to scour, but I can't claim that I have a solid understanding of everything I've read so far.
If there is an additional verification step that I have missed, how does that fit into this (client_credentials) flow?
Thanks,
Carrie
I've found 2 different ways of using this token to gain access.
Include the access token in the request header. OR
Include the access token as a form parameter
This means that your access token is of type Bearer token (therefore, you can also use a third way to send the access token: using GET method with a query parameter).
what are the chances that an access token could be obtained or intercepted?
Or, are there methods included in this flow (or perhaps another OAuth flow)
that also include client verification?
You have a Bearer token, thus the RFC-6750 applies. The Threat Mitigation section answers your questions:
first, your access token may be disclosed if the version of TLS between your client app and the authorization server (to get the token), and between the client app and the resource server (to give the token), have a security flaw (excerpt: This requires that the communication interaction between the client and the authorization server, as well as the interaction between the client and the resource server, utilize confidentiality and integrity protection. Since TLS is mandatory to implement and to use with this specification, it is the preferred approach for preventing token disclosure via the communication channel.)
secondly, another way for your access token to be disclosed is when using a TLS accelerator. As said in the same section of the RFC: In some deployments, including those utilizing load balancers, the TLS connection to the resource server terminates prior to the actual server that provides the resource. This could leave the token unprotected between the front-end server where the TLS connection terminates and the back-end server that provides the resource.
There are other ways to get the access token disclosed.
The solution is not to implement another OAuth flow but to apply the recommandations in section 5.3 of the RFC. As a summary, the main recommandations are:
always use TLS,
validate TLS certificate chains,
use token encryption in addition to the usage of TLS protection: for instance, encrypt the token with a shared secret between the client app and the resource server,
don't store bearer tokens in cookies,
issue short-lived bearer tokens,
issue scoped bearer tokens (use audience restriction).
This is not in the RFC but I would add this recommandation: use mutual authentication. This means that the client app must have a X.509 certificate that must be verified by the resource server. This is possible in the specific Oauth2 flow you have chosen, because the client app is known by the resource server (with some alternative flows, it can not be done).
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