How do you call an authenticated ServiceStack service once the client is authenticated using OAuth? - servicestack

Lets say I have a web client (i.e. MVC 4 client) that authenticates users using an oAuth provider (i.e. Facebook, Google etc).
I want to call another web service in my client logic, and that web service also authenticates with oAuth providers.
What would the web service request look like from the client? What do I need to pass to the web service?

I suggest you review this question, How do I authorize access to ServiceStack resources using OAuth2 access tokens via DotNetOpenAuth?. The poster provided his final solution, including a link to a sample solution, which he has graciously open sourced. The client side code, for his solution, looks like this:
// Create the ServiceStack API client and the request DTO
var apiClient = new JsonServiceClient("http://api.mysite.com/");
var apiRequestDto = new Shortlists { Name = "dylan" };
// Wire up the ServiceStack client filter so that DotNetOpenAuth can
// add the authorization header before the request is sent
// to the API server
apiClient.LocalHttpWebRequestFilter = request => {
// This is the magic line that makes all the client-side magic work :)
ClientBase.AuthorizeRequest(request, accessTokenTextBox.Text);
}
// Send the API request and dump the response to our output TextBox
var helloResponseDto = apiClient.Get(apiRequestDto);
Console.WriteLine(helloResponseDto.Result);
A similar solution is provided here: https://stackoverflow.com/a/13791078/149060 which demonstrates request signing as per OAuth 1.0a
var client = new JsonServiceClient (baseUri);
client.LocalHttpWebRequestFilter += (request) => {
// compute signature using request and a previously obtained
// access token
string authorization_header = CalculateSignature (request, access_token);
request.Headers.Add ("Authorization", authorization_header);
};
var response = client.Get<MySecuredResponse> ("/my/service");
You will, of course, need to adjust to fit the requirements of your OAuth providers, i.e. signing, token, etc.

Related

How to store access token? (Oauth 2, Auth code flow)

From what i understand the purpose of the Authorization Code flow is to exchange the auth code for access token. This exchange happens between the server which serves the page and authorization server so that the actual access token is not exposed to the client user.
How should the page server store the access token once it is obtained? I was learning from a Pluralsight example in which there is this part of code:
public static HttpClient GetClient()
{
HttpClient client = new HttpClient();
var accessToken = RequestAccessTokenAuthorizationCode();
client.SetBearerToken(accessToken);
client.BaseAddress = new Uri(IdentityConstants.API);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
private static string RequestAccessTokenAuthorizationCode()
{
// did we store the token before?
var cookie = HttpContext.Current.Request.Cookies.Get("ClientMVCCookie.AuthCode");
if (cookie != null && cookie["access_token"] != null && !string.IsNullOrEmpty(cookie["access_token"]))
{
return cookie["access_token"];
}
// no token found - request one
// we'll pass through the URI we want to return to as state
var state = HttpContext.Current.Request.Url.OriginalString;
var authorizeRequest = new IdentityModel.Client.AuthorizeRequest(
IdentityConstants.AuthEndoint);
var url = authorizeRequest.CreateAuthorizeUrl(IdentityConstants.MVCClientSecret, "code", "management secret",
IdentityConstants.MVCAuthCodeCallback, state);
HttpContext.Current.Response.Redirect(url);
return null;
}
}
This will cause each request to check if there is an access token stored in the cookie. If not then the flow will be initiated. The callback looks like this:
public class CallbackController : Controller
{
// GET: STSCallback
public async Task<ActionResult> Index()
{
// get the authorization code from the query string
var authCode = Request.QueryString["code"];
// with the auth code, we can request an access token.
var client = new TokenClient(
IdentityConstants.TokenEndoint,
"mvc_client_auth_code",
IdentityConstants.MVCClientSecretAuthCode);
var tokenResponse = await client.RequestAuthorizationCodeAsync(
authCode,
IdentityConstants.MVCAuthCodeCallback);
// we save the token in a cookie for use later on
var cookie = Response.Cookies["ClientMVCCookie.AuthCode"];
cookie.Expires = DateTime.Now.AddMinutes(1);
cookie["access_token"] = tokenResponse.AccessToken;
// get the state (uri to return to)
var state = Request.QueryString["state"];
// redirect to the URI saved in state
return Redirect(state);
}
}
Doesn't storing the access token in the cookie defeath the whole purpose of the authorization code flow? The cookie will be transmitted to the client browser thus exposing it to the client? Am i missing something? It this is not the correct way to store the token, how should it be stored?
The client, in OAuth terminology, is the component that makes requests to the resource server, in your case, the client is the server of a web application (NOT the browser).
Therefore, the access token should be stored on the web application server only. It should not be exposed to the browser, and it doesn't need to, because the browser never makes any direct requests to the resource server. It talks to the web application server instead, which in turn makes requests to the resource server using the access token.
How the browser authenticates itself with the web application server has nothing to do with OAuth 2.0. For example, it might be a regular session cookie, and the web application server might associate each session or each user with an access token.
The token request, which exchanges the authentication code for an access token, is done by the web application server, and the web application server should authenticate itself with the authorization server (e.g., using a shared client_secret).
Authorization code flow ensures that the client can be authenticated, which protects against malicious clients posing as legitimate clients. Not all web application clients have a server component, and in some cases, requests to the resource server are made directly by JavaScript code in the browser. In such situations, the browser is the client, and the access token must be stored by the browser (in a JavaScript variable, local storage or a cookie). In this case, the client cannot be authenticated (but a reasonable amount of security may be achieved by using TLS and the server redirecting only to registered endpoint URLs).
Recommended reading regarding OAuth 2.0 security: https://www.rfc-editor.org/rfc/rfc6819#section-4.3.3 (RFC 6819)
The cookie is never exposed to the browser. It is part of the response returned from the authorization server to the client, which itself is a server, not a browser. The CallbackController, which implements the redirection endpoint, extracts the cookie from the response.
The cookie is never passed on to the browser. How the browser authenticates itself with the client's application server is not shown in your sample code, and it is not part of OAuth.
The authorization server could store the token in the request body (e.g., in JSON format) rather than in a cookie. However, this makes no difference, because the client can see and process the entire response anyway.
For details, see my other answer: https://stackoverflow.com/a/44655679/2279059
Side note: The CallbackController uses state to store a final URL to redirect the browser to. This is non-standard but works. However, state is actually meant to protect the redirection endpoints against CSRF attacks. The CallbackController does not validate state but blindly redirects to whatever URL was given. Probably this detail was left out, because the code was meant as an example. However, it shows that this code is probably not entirely production-ready.
If you are going to request a rest resource from the browser, the flow you need is Implicit Grant. Check this Auth0 post to decide between the flows https://auth0.com/docs/api-auth/which-oauth-flow-to-use. If you want to use the access token from server you should store the Authorization code and generate an access token every time you need it, access token is not intended to live more than 5 minutes, you don't need to store it

Auth0 authentication of single-page-app on a different domain than the api

I'm trying add Auth0 authentication to my single-page-app. My app is running under a domain, say app.mycompany.com, whereas the api used by this app is running under a different domain, say api.mycompany.com.
I'm aware of this thread:
Single Sign On (SSO) solution/architecture for Single Page App (SPA)
and the auth0 articles and github repositories linked by here. But I have a feeling that my scenario is slightly simpler, as I don't necessarily want to have single-sign-on between several different single-page-apps. For a start I just want the seperation between the API and the app.
Here is what I have tried already:
I already started from the article React Login With Auth0 and downloaded the starter project. I can surely login without problems and it will leave me with an id_token in my localStorage containing a JWS issued by Auth0.
I can also login directly on api.mycompany.com (my FeathersJS API application) and I can see that during the OAuth redirecting process, the id_token token is magically translated to a feathers-jwt token issued by my Feathers application containing the internal ID of the user-object matching the auth0-ID. I also have implemented the logic used to map from the Auth0-ID to my internal ID. Furthermore all my Feathers hooks such as validation of token and population of the user are working.
What I cannot figure out is how to alter the react-application running under app.mycompany.com with an Auth0-token in localStorage, so that this token is translated to a feathers-jwt token by api.mycompany.com, in such a way that all succeeding API-calls automatically has the feathers-jwt token included so the API can validate the user and return the right data.
Any suggestions on how to proceed will be greatly appreciated.
A couple of more background details:
The api is built on node.js and featherjs (which basically is an extension of Express)
The single-page-app is built on ReactJS and is served by a simple Express server, but it could be served by any server that can serve static files over http. The single-page-app makes http-requests to the api to read data and perform operations.
The api has the following lines of code taking care of the authentication:
const authentication = require('feathers-authentication');
const Auth0Strategy = require('passport-auth0').Strategy;
app.configure(authentication({
local:false,
token: {
secret: 'mysecret',
payload: ['email', 'auth0Nickname'],
issuer: 'mycompany'
},
idField: 'id',
shouldSetupSuccessRoute: false,
auth0: {
strategy: Auth0Strategy,
domain: 'mycompany.eu.auth0.com',
'clientID': 'xxx',
'clientSecret': 'yyy'
}
}));
I had exactly the same problem as you, I wanted to authenticate a user from a single page application, calling the API located on an other server.
The official auth0 example is a classic Express web application that does authentication and renders html page, but it's not a SPA connected to an API hosted on an other domain.
Let's break up what happens when the user authenticates in this example:
The user makes a request calling /auth/auth0 route
The user is automatically redirected to the Auth0 authentication process (Auth0 login form to choose the provider and then the provider login screen)
The user is redirected to /auth/success route
/auth/success route redirects to the static html page public/success.html, also sending a jwt-token cookie that contains the user's token
Client-side, when public/success.html loads, Feathers client authenticate() method reads the token from the cookie and saves it in the local storage.
From now, the Feathers client will authenticate the user reading the cookie from the local storage.
I tried to adapt this scenario to a single-page application architecture, implementing the following process:
From the SPA, call the authentication API with a source query string parameter that contains the SPA URL. For example: http://my-api.com/auth/auth0?source=http://my-spa.com
Server-side, in /auth/auth0 route handler, create a cookie to store that URL
After a successful login, read the source cookie to redirect the user back to the SPA, sending the JWT token in a cookie.
But the last step didn't work because you can't set a cookie on a given domain (the API server domain) and redirect the user to an other domain! (more on this here on Stackoverflow)
So actually I solved the problem by:
server-side: sending the token back to the client using the URL hash.
client-side: create a new html page that reads the token from the URL hash
Server-side code:
// Add a middleware to write in a cookie where the user comes from
// This cookie will be used later to redirect the user to the SPA
app.get('/auth/auth0', (req, res, next) => {
const { origin } = req.query
if (origin) {
res.cookie(WEB_CLIENT_COOKIE, origin)
} else {
res.clearCookie(WEB_CLIENT_COOKIE)
}
next()
})
// Route called after a successful login
// Redirect the user to the single-page application "forwarding" the auth token
app.get('/auth/success', (req, res) => {
const origin = req.cookies[WEB_CLIENT_COOKIE]
if (origin) {
// if there is a cookie that contains the URL source, redirect the user to this URL
// and send the user's token in the URL hash
const token = req.cookies['feathers-jwt']
const redirectUrl = `${origin}/auth0.html#${token}`
res.redirect(redirectUrl)
} else {
// otherwise send the static page on the same domain.
res.sendFile(path.resolve(process.cwd(), 'public', 'success.html'))
}
})
Client-side, auth0.html page in the SPA
In the SPA, I created a new html page I called auth0.html that does 3 things:
it reads the token from the hash
it saves it in the local storage (to mimic what the Feathers client does)
it redirects the user to the SPA main page index.html
html code:
<html>
<body>
<script>
function init() {
const token = getToken()
if (!token) {
console.error('No auth token found in the URL hash!')
}
// Save the token in the local storage
window.localStorage.setItem('feathers-jwt', token)
// Redirect to the single-page application
window.location.href = '/'
}
// Read the token from the URL hash
function getToken() {
const hash = self.location.hash
const array = /#(.*)/.exec(hash)
if (!array) return
return array[1]
}
init()
</script>
</body>
</html>
And now in the SPA I can use the Feathers client, reading the token from the local storage when the app starts.
Let me know if it makes sense, thank you!
If you haven't done so, you should follow this article (React Login with Auth0) to implement the authentication on your React application. If you already tried to follow it, update your question with specific issues you faced.
Even though you currently not need SSO, the actual implementation of the authentication in your application will not vary much. By using Auth0 enabling SSO across your apps is mostly enabling configuration switches.
Finally for a full reference with all the theory behind the security related aspects of your exact scenario check:
Auth0 Architecture Scenarios: SPA + API
Update:
The full scenario I linked too covers the most comprehensive scenarios where an API is accessed by a multitude of client applications that may even be developed by third-parties that do not own the protected API, but want to access the data behind it.
It does this by leveraging recent features that are currently only available in the US region and that at a very high level can be described as an OAuth 2.0 authorization server delivered as a service.
Your particular scenario is simpler, both the API and client application are under control of the same entity, so you have another option.
Option 1 - Leverage the API authorization through Auth0 US region only (for now)
In this situation your client application, at authentication time, would receive an id_token that would be used to know the currently authenticated user and would also receive an access_token that could be used to call the API on behalf of the authenticated user.
This makes a clear separation between the client application and the API; the id_token is for client application usage and the access_token for API usage.
It has the benefit that authorization is clearly separated from authentication and you can have a very fine-grained control over authorization decisions by controlling the scopes included in the access token.
Option 2 - Authenticate in client application and API in the same way
You can deploy your client application and API separately, but still treat them from a conceptual perspective as the same application (you would have one client configured in Auth0 representing both client-side and API).
This has the benefit that you could use the id_token that is obtained after authentication completes to know who the user was on the client-side and also as the mechanism to authenticate each API request.
You would have to configure feathers API to validate the Auth0 id_token as an accepted token for accessing the API. This means that you don't use any feathers based on authentication on the API, that is, you just accept tokens issued by Auth0 to your application as the way to validate the access.

What authentication strategy to use? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Recently I have been reading up on OAuth2, OpenID Connect etc. But still very lost at what to use when and how to implement it. I am thinking of using NodeJS for now.
Lets say I want to create a blog service. This service will expose API's for clients to use. "Clients" include an admin CMS. I am thinking it will be nice to decouple my server and client (UI). I can change the UI without touching the server. These clients are likely going to be single page web applications.
Ok 1st question: In this example, should I be using OAuth2? Why? Isit just because I am authorizing the admin app to access by blog?
Since its SPA's, I think the right strategy is OAuth2 Implicit Flow?
For each app, eg. admin cms, I will have to generate an AppID which is passed to the auth server. No app secret is required correct?
Isit possible to use google login in this case (instead of username/password)? Does OpenID connect do this?
How do I implement all these in NodeJS? I see https://github.com/jaredhanson/oauth2orize, but I do not see how to implement the implicit flow.
I do see an unofficial example https://github.com/reneweb/oauth2orize_implicit_example/blob/master/app.js, but what I am thinking is why is sessions required? I thought one of the goals of tokens is so that server can be stateless?
I am also wondering, when should I use API key/secret authentication?
Let's examine your questions
Should I be using OAuth2? Why?
A: Well, as today the old OpenId 2 authentication protocol has been marked as obsolete (November 2014) and OpenId Connect is an identity layer built on top of OAuth2 so the real question is if is important for you and your business to know and verify the identity of your users (the authentication part). If the answer is "yes" then go for OpenId Connect otherwise you can choose any of the two, the one you feel more comfortable with.
Since its SPA's, I think the right strategy is OAuth2 Implicit Flow?
A: Not really. You can implement any strategy when using a SPA, some takes more work than others and greatly depends on what are you trying to accomplish. The implicit flow is the simplest but it does not authenticate your users since an access token is issued directly.
When issuing an access token during the implicit grant flow, the authorization server does not authenticate the client. In some cases, the client identity can be verified via the redirection URI used to deliver the access token to the client.
I would not recommend this flow for your app (or any app that needs a decent level of security1).
If you want to keep it simple you should use Resource Owner Grant flow with an username and password but again there is nothing that prevents you of implementing the Authorization Code Grant flow especially if you want to allow third parties apps to use your service (which in my opinion is a winning strategy) and it will be relatively more secure than the others since it requires explicit consent from the user.
For each app, eg. admin cms, I will have to generate an AppID which is passed to the auth server. No app secret is required correct?
A: Yes that is correct but the client_secret can be used to add an extra layer of security to the token endpoint in the resource owner flow when you can't use Basic authentication, this is not required in any other flow.2 3
The authorization server MUST:
require client authentication for confidential clients or for any
client that was issued client credentials (or with other
authentication requirements),
authenticate the client if client authentication is included, and
validate the resource owner password credentials using its
existing password validation algorithm.
and
Alternatively, the authorization server MAY support including the client credentials in the request-body (...) Including the client credentials in the request-body using the two parameters is NOT RECOMMENDED and SHOULD be limited to clients unable to directly utilize the HTTP Basic authentication scheme (or other password-based HTTP authentication schemes)
Is it possible to use google login in this case (instead of username/password)? Does OpenID connect do this?
A: Yes, is possible to use google login in which case you are just delegating the authentication and authorization job to the google servers. One of the benefits of working with an authorization server is the ability to have a single login to access other resources without having to create a local account for each of the resources you want to access.
How do I implement all these in NodeJS?
Well you started with the right foot. Using oaut2horize is the most simple way to implement an authorization server to issue tokens. All other libraries I tested were too complicated of use and integrate with node and express (disclaimer: this is just my opinion). OAuthorize plays nicely with passport.js(both from the same author) which is a great framework to enforce the authentication and authorization with over 300+ strategies like google, facebook, github, etc. You can easily integrate google using passport-google(obsolete), passport-google-oauth and passport-google-plus.
Let's go for the example
storage.js
// An array to store our clients. You should likely store this in a
// in-memory storage mechanism like Redis
// you should generate one of this for any of your api consumers
var clients = [
{id: 'as34sHWs34'}
// can include additional info like:
// client_secret or password
// redirect uri from which client calls are expected to originate
];
// An array to store our tokens. Like the clients this should go in a memory storage
var tokens = [];
// Authorization codes storage. Those will be exchanged for tokens at the end of the flow.
// Should be persisted in memory as well for fast access.
var codes = [];
module.exports = {
clients: clients,
tokens: tokens,
codes: codes
};
oauth.js
// Sample implementation of Authorization Code Grant
var oauth2orize = require('oauth2orize');
var _ = require('lodash');
var storage = require('./storage');
// Create an authorization server
var server = oauth2orize.createServer();
// multiple http request responses will be used in the authorization process
// so we need to store the client_id in the session
// to later restore it from storage using only the id
server.serializeClient(function (client, done) {
// return no error so the flow can continue and pass the client_id.
return done(null, client.id);
});
// here we restore from storage the client serialized in the session
// to continue negotiation
server.deserializeClient(function (id, done) {
// return no error and pass a full client from the serialized client_id
return done(null, _.find(clients, {id: id}));
});
// this is the logic that will handle step A of oauth 2 flow
// this function will be invoked when the client try to access the authorization endpoint
server.grant(oauth2orize.grant.code(function (client, redirectURI, user, ares, done) {
// you should generate this code any way you want but following the spec
// https://www.rfc-editor.org/rfc/rfc6749#appendix-A.11
var generatedGrantCode = uid(16);
// this is the data we store in memory to use in comparisons later in the flow
var authCode = {code: generatedGrantCode, client_id: client.id, uri: redirectURI, user_id: user.id};
// store the code in memory for later retrieval
codes.push(authCode);
// and invoke the callback with the code to send it to the client
// this is where step B of the oauth2 flow takes place.
// to deny access invoke an error with done(error);
// to grant access invoke with done(null, code);
done(null, generatedGrantCode);
}));
// Step C is initiated by the user-agent(eg. the browser)
// This is step D and E of the oauth2 flow
// where we exchange a code for a token
server.exchange(oauth2orize.exchange.code(function (client, code, redirectURI, done) {
var authCode = _.find(codes, {code: code});
// if the code presented is not found return an error or false to deny access
if (!authCode) {
return done(false);
}
// if the client_id from the current request is not the same that the previous to obtain the code
// return false to deny access
if (client.id !== authCode.client_id) {
return done(null, false);
}
// if the uris from step C and E are not the same deny access
if (redirectURI !== authCode.uri) {
return done(null, false);
}
// generate a new token
var generatedTokenCode = uid(256);
var token = {token: generatedTokenCode, user_id: authCode.user_id, client_id: authCode.client_id};
tokens.push(token);
// end the flow in the server by returning a token to the client
done(null, token);
}));
// Sample utility function to generate tokens and grant codes.
// Taken from oauth2orize samples
function uid(len) {
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var buf = []
, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
, charlen = chars.length;
for (var i = 0; i < len; ++i) {
buf.push(chars[getRandomInt(0, charlen - 1)]);
}
return buf.join('');
}
module.exports = server;
app.js
var express = require('express');
var passport = require('passport');
var AuthorizationError = require('oauth2orize').AuthorizationError;
var login = require('connect-ensure-login');
var storage = require('./storage');
var _ = require('lodash');
app = express();
var server = require('./oauthserver');
// ... all the standard express configuration
app.use(express.session({ secret: 'secret code' }));
app.use(passport.initialize());
app.use(passport.session());
app.get('/oauth/authorize',
login.ensureLoggedIn(),
server.authorization(function(clientID, redirectURI, done) {
var client = _.find(storage.clients, {id: clientID});
if (client) {
return done(null, client, redirectURI);
} else {
return done(new AuthorizationError('Access denied'));
}
}),
function(req, res){
res.render('dialog', { transactionID: req.oauth2.transactionID, user: req.user, client: req.oauth2.client });
});
app.post('/oauth/authorize/decision',
login.ensureLoggedIn(),
server.decision()
);
app.post('/oauth/token',
passport.authenticate(['basic', 'oauth2-client-password'], { session: false }),
server.token(),
server.errorHandler()
);
(...) but what I am thinking is why is sessions required? I thought one of the goals of tokens is so that server can be stateless?
When a client redirects a user to user authorization endpoint, an authorization transaction is initiated. To complete the transaction, the user must authenticate and approve the authorization request. Because this may involve multiple HTTP request/response exchanges, the transaction is stored in the session.
Well yes, but the session is used for the token negotiation process. Later you enforce authorization sending the token in an Authorization header to authorize each request using the obtained token.
In my experience, OAuth2 is the standard way of securing APIs. I'd recommend using OpenID Connect though as it adds authentication to OAuth2's otherwise authorization-based spec. You can also get Single-Sign-On between your "clients".
Since its SPA's, I think the right strategy is OAuth2 Implicit Flow?
De-coupling your clients and servers is a nice concept (and I'd generally do the same too) however, I'd recommend the authorization code flow instead as it doesn't expose the token to the browser. Read http://alexbilbie.com/2014/11/oauth-and-javascript/. Use a thin server-side proxy instead to add the tokens to the request. Still, I generally avoid using any server-generated code on the client (like JSPs in java or erb/haml in rails) since it couples the client to the server too much.
For each app, eg. admin cms, I will have to generate an AppID which is passed to the auth server. No app secret is required correct?
You'll need a client ID for implicit flow. If you use authorization code flow (recommended), you'll need both an ID and secret but the secret will be kept in the thin server-side proxy rather than a client-side only app (since it can't be secret in that case)
Is it possible to use google login in this case (instead of username/password)? Does OpenID connect do this?
Yes. Google uses openid connect
How do I implement all these in NodeJS? I see https://github.com/jaredhanson/oauth2orize, but I do not see how to implement the implicit flow.
A nice thing about openid connect is that (if you use another provider like google), you don't have to implement the provider yourself and you'll only need to write client code (and/or utilize client libaries). See http://openid.net/developers/libraries/ for different certified implementations. See https://www.npmjs.com/package/passport-openidconnect for nodejs.

Oauth2 flow without redirect_uri

I am creating an Android/iOS app which communicates with a Node.js server and would like to identify them securely on my server using Google (and/or Facebook) and OAuth2. I've looked at the following documentation: https://developers.google.com/+/web/signin/server-side-flow
I do not need authorization, I only need authentication (I only want to make sure that the person calling my Node.js service is the person they say they are). To achieve this, if I understand properly, I have to let the user log in using Google on the client side, this will give them an authorization_code which they can then give to my server. My server can then exchange that code for an access_token, and therefore retrieve information about the user. I am then guaranteed that the user is the person they say they are.
The Google documentations (link above) says: "In the Authorized redirect URI field, delete the default value. It is not used for this case.", however, for my server to exchange the authorization_code for an access_token, it needs to provide a redirect_uri, am I missing something?
The redirect_uri is useless for Unity games, for instance (since logging in with Google simply opens a new "window", which is closed when logged in, no redirection involved).
TL;DR
How do you use OAuth2 to authenticate users between my client and my server without redirection?
TL;DR How do you use OAuth2 to authenticate users between my client and my server without redirection?
You can't. OAuth requires that the user is directed to an authorization (and possibly login) screen, and then redirected back to your app.
EDIT 20/12/22. See comment below regarding latest status
Have you looked at this documentation? https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi
Choosing a redirect URI
When you create a client ID in the Google Developers Console, two
redirect_uris are created for you: urn:ietf:wg:oauth:2.0:oob and
http://localhost. The value your application uses determines how the
authorization code is returned to your application.
http://localhost
This value signals to the Google Authorization Server that the
authorization code should be returned as a query string parameter to
the web server on the client. You may specify a port number without
changing the Google Developers Console configuration. To receive the
authorization code using this URL, your application must be listening
on the local web server. This is possible on many, but not all,
platforms. If your platform supports it, this is the recommended
mechanism for obtaining the authorization code.
I had this problem and it took me ages to find the "postmessage" solution that Nepoxx mentions in the comments of the accepted answer here.
For clarification, here's what worked for me.
Follow steps 1-6 here: https://developers.google.com/identity/sign-in/web/server-side-flow
Install googleapis library npm install --save googleapis
For the server-side token exchange do this:
var googleapis = require('googleapis');
var OAuth2 = googleapis.auth.OAuth2;
var oauth2Client = new OAuth2(
GOOGLE_SSO_CLIENT_ID,
GOOGLE_SSO_CLIENT_SECRET,
'postmessage' // this is where you might otherwise specifiy a redirect_uri
);
oauth2Client.getToken(CODE_FROM_STEP_5_OF_INSTRUCTIONS, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
});
The redirect_uri can be a URL with a custom URL scheme for which the client registered a handler. This is described here: What's a redirect URI? how does it apply to iOS app for OAuth2.0?. It is not so much about "redirecting" it is about a callback endpoint to your app.
And it become really easy if you use VueJS with https://github.com/guruahn/vue-google-oauth2
Client side
import GAuth from 'vue-google-oauth2'
Vue.use(GAuth, {
clientId: 'xxxxxxx.apps.googleusercontent.com',
scope: 'profile',
})
async signWithGoogle() {
const code = await this.$gAuth.getAuthCode() //
console.log(code ) // { code: 'x/xxxxxxxxxx' }
// send the code to your auth server
// and retrieve a JWT or something to keep in localstorage
// to send on every request and compare with database
}
Server side
import { google } from 'googleapis'
const oauth2Client = new google.auth.OAuth2(GOOGLE_ID, GOOGLE_SECRET, 'postmessage')
google.options({ auth: oauth2Client })
async function getAccount(code) {
// the code you sent with the client
const { tokens } = await oauth2Client.getToken(code)
oauth2Client.setCredentials(tokens)
const oauth2 = google.oauth2({ version: 'v2' })
const { data: { id } } = await oauth2.userinfo.get()
// there you have the id of the user to store it in the database
// and send it back in a JWT
}

Securely calling a WebSite hosted Web API from an Azure WebJob

I have a continuously scheduled web job that's monitoring a message queue, pulling messages off and calling a Web API on the peer Web Site to process the messages (in this case using SignalR to send notifications to appropriate users).
What would be the best way in this case to call the web API securely? The API being hosted in the web site is obviously exposed otherwise. Perhaps something using Basic Auth or storing a security token in config and passing it from the job to the web API. Or creating a custom AuthorizeAttribute?
Ant thoughts on securing the Web API call from the WebJob would be much appreciated. The API should only be callable from the WebJob.
UPDATE:
Something like this perhaps?
First I declare this class;
public class TokenAuthenticationHeaderValue : AuthenticationHeaderValue
{
public TokenAuthenticationHeaderValue(string token)
: base("Token", Convert.ToBase64String(Encoding.UTF8.GetBytes(token)))
{ }
}
Then the caller (the WebJob) uses this class to set an auth header when making the HTTP request;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(/* something */);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new TokenAuthenticationHeaderValue("TOKEN FROM CONFIG");
// ....
Over in the Web API we check the request looking for the expected token in the auth header, currently the code is pretty ugly but this could be put into a custom attribute;
public HttpResponseMessage Post([FromBody]TheThing message)
{
var authenticationHeader = Request.Headers.Authorization;
var token = Encoding.UTF8.GetString(Convert.FromBase64String(authenticationHeader.Parameter));
if (authenticationHeader.Scheme != "Token" || token != "TOKEN FROM CONFIG")
{
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "No, no, no. That's naughty!");
}
// All OK, carry on.
So this way the WebJob calls the Web API on the peer web site and security is achieved by passing a token that is securely held in the Azure configuration, both the Site and Job have access to this token.
Any better ideas?
Sounds like Basic Authentication would be fine for your scenario.
Great tutorial here: Basic Authentication

Resources