It seems increasingly difficult to reopen makeRequest in ember-cli. I realize that client_secret is mostly redundant in a browser application, but some OAuth2 servers expect it regardless of whether the client is public or confidential.
Would it be possible to just add these specific items to the payload (or set the appropriate header) if they are present in the user's config?
This is what I'm doing in app/app.js:
import OAuth2 from 'simple-auth-oauth2/authenticators/oauth2';
OAuth2.reopen({
makeRequest: function(data) {
var client_id = MyProjectENV.APP.api_client_id;
var client_secret = MyProjectENV.APP.api_client_secret;
return Ember.$.ajax({
url: this.serverTokenEndpoint,
type: 'POST',
data: data,
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
headers: { "Authorization": "Basic " + btoa(client_id + ":" + client_secret) }
});
}
});
I'm strongly against using client_secret with a JS client as it's completely pointless. Once you deploy the app the client_secret is no longer a secret. That was the main reason why it's not supported in Ember Simple Auth - it might lead people to actually treating it as a secret or to assume that when the client_id and client_secret are transmitted to the server correctly then the client must be the Ember app while it could be any client as client_id and client_secret are public on the internet.
If you really want to use it though the better approach is to use a custom authenticator and simply add client_id and client_secret in the makeRequest method.
I understand that using client_secret with a public client seems redundant. It does remain, however, part of RFC6749 § 2.3 which states:
The authorization server MAY establish a client authentication method with public clients.
Since it is part of the specification, I see no reason not to support it (even if you choose not to promote it). Why not add the headers if the appropriate configuration variables are set? e.g. in oauth2#init:
this.clientId = globalConfig.clientId || "";
this.clientSecret = globalConfig.clientSecret || "";
and in oauth2#makeRequest:
if (this.clientId !== "" && this.clientSecret !== "") {
headers = { "Authorization": "Basic " + btoa(this.clientId + ":" + this.clientSecret) };
} else {
headers = {};
}
return Ember.$.ajax({
url: this.serverTokenEndpoint,
headers: headers,
...
I don't see a reason to put the burden of overriding the method on the user when the behavior is explicitly outlined in the Proposed Standard.
I think that part of the specification just doesn't apply to JS clients. Also the question is what the benefit of using it would be. As the information could not be trusted anyway on the server side as the key is publicly available on the internet and thus the information could actually not be used on the server I'd say there would be no benefit with supporting it. There would be the drawback though that it might make people believe they can safely assume that the information does actually identify the client which actually might be wrong and lead to security problems.
Given how easy it is to define a custom authenticator that overrides the makeRequest method for people who really want to use client identification (and I really cannot think of any reason why anyone would want that), I'd say that's enough support.
And adding client_id and client_secret in a custom authenticator really is as easy as:
import Authenticator from 'simple-auth-oauth2/authenticators/oauth2';
export default Authenticator.extend({
makeRequest: function(data) {
data.client_id = …;
data.client_secret: …;
return this._super(data);
}
})
Where would the code below be added? (I'm new to ember-cli)
import Authenticator from 'simple-auth-oauth2/authenticators/oauth2';
export default Authenticator.extend({
makeRequest: function(data) {
data.client_id = …;
data.client_secret: …;
return this._super(data);
}
})
@MaazAli that code would go in app/authenticators/my-customer-authenticator.js
@dustinfarris I've done that but the extra parameters don't get sent in the request. The authenticators folder was not in the app folder and so I created one (I assume you're allowed to do that in ember-cli). Aside from creating it and adding the following file in it I didn't do any thing else.
// app/authenticators/oauth-custom.js
import Authenticator from 'simple-auth-oauth2/authenticators/oauth2';
export default Authenticator.extend({
makeRequest: function(data) {
data.client_id = "izjb0t4jEQ4HvZ2oaaze";
data.client_secret = "GQ4TiUfISAMPnlObzV22";
return this._super(data);
}
});
// app/routes/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Route.extend(LoginControllerMixin, {
authenticator: 'simple-auth-authenticator:oauth2-password-grant'
});
Keeping the login.js same as before doesn't change anything, the parameters remain the same.
So I tried the following, which also gave me errors
// app/routes/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Route.extend(LoginControllerMixin, {
authenticator: 'simple-auth-authenticator:oauth-custom'
});
But that gives me errors saying the authenticate function is not implemented.
So what's the right way of adding this code?
@MaazAli You need to add an initializer for your custom authenticator.
e.g.
// app/initializers/oauth-custom.js
import OAuthCustomAuthenticator from 'myproject/authenticators/oauth-custom';
export default {
name: 'oauth-custom',
initialize: function(container) {
container.register(
'oauth-custom:oauth2-password-grant',
OAuthCustomAuthenticator);
}
};
and then change the authenticator property of your LoginRoute, e.g.:
export default Ember.Route.extend(LoginControllerMixin, {
authenticator: 'oauth-custom:oauth2-password-grant',
});
I implemented the changes and now there is no "form data" being sent in the request.
So I investigated this further and added some console.logs in my /authenticators/oauth-custom.js file.
So I just logged the data variable and the data.client_id variable. I would have expected the data variable to be an object but apparently it's just a url and data.client_id is undefined. I'm a little confused as to why that is
/token front/authenticators/oauth-custom.js:12
undefined
Here is the code in that file
import Authenticator from 'simple-auth-oauth2/authenticators/oauth2';
export default Authenticator.extend({
makeRequest: function(data) {
data.client_id = "izjb0t4jEQ4HvZ2oaaze";
data.client_secret = "GQ4TiUfISAMPnlObzV22";
data.scope = "guest";
console.log(data);
console.log(data.client_id);
return this._super(data);
}
});
@MaazAli check out the original makeRequest method. It accepts url and data.
@dustinfarris that helps a lot, so additional parameters seem to come in but password is "null" and username is "undefined"
Related question SO question: http://stackoverflow.com/questions/24699086/ember-simple-auth-oauth-2-0-not-sending-the-right-parameters
I guess only the login.hbs is relevant, the rest of the code is the one found here.
You're including the LoginControllerMixin into a route which doesn't work. Make sure you're using a controller.
@marcoow, can't believe I missed that. I've patched up that issue but an error still remains
Uncaught TypeError: Cannot read property 'send' of undefined
Ember.Route.Ember.Object.extend.send vendor.js:49453
superWrapper vendor.js:14443
runRegisteredAction vendor.js:51663
Backburner.run vendor.js:19400
Ember.run vendor.js:19815
handleRegisteredAction vendor.js:51661
(anonymous function) vendor.js:35410
jQuery.event.dispatch vendor.js:4736
jQuery.event.add.elemData.handle
I'm pretty sure this has to do with the router, but I don't know what exactly.
Here's what I have
// routes/login.js
import Ember from 'ember';
export default Ember.Route.extend();
// controllers/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Route.extend(LoginControllerMixin, {
authenticator: 'simple-auth-authenticator:oauth2-password-grant',
});
templates/login.hbs
<div class="container" style="width: 500px; margin-top: 200px;">
<form {{action authenticate on='submit'}} class="form-horizontal" role="form">
<div class="form-group">
<label for="identification" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
{{input id='identification' class="form-control" placeholder='Enter Username' value=identification}}
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
{{input id='password' class="form-control" placeholder='Enter Password' type='password' value=password}}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button>
</div>
</div>
</form>
</div>
the controller is still wrong, you need to extend Ember.Controller. Please continue with further discussions on stackoverflow.com as this is meant to be for issues only.
@marcoow does it not make sense to atleast keep the ability to pass in the client_id, even without the client_secret?
i understand your reasoning behind not wanting to keep client_secret, but for a "Resource Owner Password" flow, you do not need to provide a secret, but you still need a client_id
@karabijavad I don't think the client_id per se for the grant type. Given the fact that everybody seems to want to have it though I think I'd accept a pull request - I'd say it would have to be opt-in though and the docs should clearly state that it cannot be trusted and should only be used for statistics etc.
+1 for adding the option to pass client_id and client_secret.
@dustinfarris Does your app/app/js method still work? What happens on my end is that an OPTION request gets sent instead of a POST. If I don't comment out the custom headers, the POST requests gets issued as expected (but of course the OAuth2 step fails).
@rotatingJazz: client_secret will not be added - search in the issues here for the reasons behind that decision.
@marcoow Yes, I understand the reasons. Personally I believe programmers should be empowered rather than assumed kinda stupid and "to be protected" but it 's your library so it 's your view that matters. Apart from the philosophical aspect though, Node.js isn't the only Node "compiler". JXCore for example offers code encryption and clients deployed that way could be considered trusted to use client_secret.
I am trying to make this work with node-oauth2-server. It requires the client_id and client_secret on the Authorization header. Unfortunately I haven't been able to make it work yet either with @dustinfarris way (see above) nor by adding it as a custom authenticator (same error).
// app/authenticators/oauth2-n.js
import Ember from 'ember';
import Authenticator from 'simple-auth-oauth2/authenticators/oauth2';
export default Authenticator.extend({
makeRequest: function (data) {
var client_id = 'myid';
var client_secret = 'public';
var headerAuth = "Basic " + btoa(client_id + ":" + client_secret);
console.log(headerAuth);
return Ember.$.ajax({
url: this.serverTokenEndpoint,
type: 'POST',
data: data,
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
headers: {
Authorization: headerAuth
}
});
}
});
// app/initializers/oauth2-n.js
import OAuthCustomAuthenticator from 'fsm/authenticators/oauth2-n';
export default {
name: 'oauth2-n',
initialize: function(container) {
container.register(
'oauth2-n:oauth2-password-grant',
OAuthCustomAuthenticator);
}
};
// app/controllers/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Controller.extend(LoginControllerMixin, {
authenticator: 'oauth2-n:oauth2-password-grant'
});
What happens instead of POST

"devDependencies": {
"body-parser": "^1.2.0",
"broccoli-asset-rev": "0.0.17",
"broccoli-ember-hbs-template-compiler": "^1.5.0",
"broccoli-merge-trees": "^0.1.4",
"broccoli-static-compiler": "^0.1.4",
"broccoli-stylus-single": "^0.1.2",
"ember-cli": "0.0.40",
"ember-cli-ember-data": "0.1.0",
"ember-cli-ic-ajax": "0.1.1",
"ember-cli-simple-auth": "^0.6.4-1",
"ember-cli-simple-auth-oauth2": "^0.6.4-1",
"express": "^4.1.1",
"glob": "^3.2.9",
"originate": "0.1.5"
}
"dependencies": {
"handlebars": "~1.3.0",
"jquery": "^1.11.1",
"qunit": "~1.12.0",
"ember-qunit": "~0.1.5",
"ember": "1.5.1",
"ember-resolver": "~0.1.1",
"loader": "stefanpenner/loader.js#1.0.0",
"ember-cli-shims": "stefanpenner/ember-cli-shims#0.0.2",
"ember-load-initializers": "stefanpenner/ember-load-initializers#0.0.2",
"ember-qunit-notifications": "^0.0.3",
"ember-cli-test-loader": "rjackson/ember-cli-test-loader#0.0.2"
},
My guess is that this has to do something with Ember Data but I 'm too new in Ember, so I 'll probably end up hijacking jQuery which is something I 'd prefer to avoid.
The point is that Ember apps will always run in the browser (at least I'm not aware of other use cases) so that their JS source is always public. Given that, it's not really only a decision to protect people from themselves but actually it doesn't make sense to use a client_secret in a public JS client as it will never actually be secret. That's my reasoning behind that: I don't see a benefit in adding anything to the library that simply doesn't provide any value.
In general I'd say that OAuth 2.0 endpoints that require both client_id and client_secret don't really support public JS clients. Anyway, if people still want to use client_secret there's an easy way to do that with Ember Simple Auth.
V8 has the snapshot feature and node-webkit supports it: https://github.com/rogerwang/node-webkit/wiki/Protect-JavaScript-source-code-with-v8-snapshot
Not sure how compatible that is with our case though, as I do not use node-webkit.
Cool, wasn't aware of that - would be interesting to know how many Ember/Ember Simple Auth projects actually use that...
Update on my issue for future visitors reference.
The OPTIONS issue was due to the cross-domain request. The browser makes a OPTIONS request first and then the actual POST. I had CORS enabled but not the proper header that explicitly allowed OPTIONS. So that fixed it.
My current code:
import Ember from 'ember';
import Authenticator from 'simple-auth-oauth2/authenticators/oauth2';
export default Authenticator.extend({
makeRequest: function (data) {
var client_id = 'myid';
var client_secret = 'public';
data.grant_type = "password";
return Ember.$.ajax({
url: this.serverTokenEndpoint,
type: 'POST',
data: data,
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
crossDomain: true,
headers: {
Authorization: "Basic " + btoa(client_id + ":" + client_secret)
}
});
}
});
The problem now is that the POST request is not passing any form data
<form {{action 'authenticate' on='submit'}}>
<label for="identification">Login</label>
{{input id='identification' placeholder='Enter Login' name="password" value=identification}}
<label for="password">Password</label>
{{input id='password' placeholder='Enter Password' type='password' name="username" value=password}}
<button type="submit">Login</button>
</form>
POST /oauth/token HTTP/1.1
Host: 127.0.0.1:6001
Connection: keep-alive
Content-Length: 33
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://127.0.0.1:4200
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.68 Safari/537.36 OPR/24.0.1558.34 (Edition Next) FirePHP/4Chrome
Authorization: Basic Zml2ZXN0YXJ0bWV3ZWJhcHA6cHVibGlj
Content-Type: application/x-www-form-urlencoded
Referer: http://127.0.0.1:4200/login
Accept-Encoding: gzip,deflate,lzma
Accept-Language: en-US,en;q=0.8
X-FirePHP-Version: 0.0.6
X-Wf-Max-Combined-Size: 262144
Form Dataview parsed
http://127.0.0.1:6001/oauth/token
I 'm probably missing something obvious at this point, if someone can spot it please post.
So, I 've resolved the issue why the authenticator is not sending the form data to the api.
makerequest 's signature is (url, data), NOT (data) as mentioned in this thread.
Source: node_modules/ember-cli-simple-auth-oauth2/vendor-addon/ember-simple-auth/simple-auth-oauth2.amd.js, line 244.
Most helpful comment
So, I 've resolved the issue why the authenticator is not sending the form data to the api.
makerequest's signature is(url, data), NOT(data)as mentioned in this thread.Source:
node_modules/ember-cli-simple-auth-oauth2/vendor-addon/ember-simple-auth/simple-auth-oauth2.amd.js, line244.