I am creating the backend in Grails that needs to support both mobile app & web app.
I have managed to use compile ':spring-security-core:2.0-RC4' for the authentication. It works fine.
Now I want to make it restful for the mobile app to call the api. So I added the following in the BuildConfig.groovy.
compile ":spring-security-rest:1.4.1", {
excludes: 'spring-security-core'
}
I am following this tutorial to use spring-security-rest.
http://alvarosanchez.github.io/grails-spring-security-rest/docs/guide/tokenStorage.html
In my app, I have User domain which extends SecUser.
Here is my Config.grrovy.
// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'm15.authentication.SecUser'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'm15.authentication.SecUserSecRole'
grails.plugin.springsecurity.authority.className = 'm15.authentication.SecRole'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
'/': ['permitAll'],
'/index': ['permitAll'],
'/index.gsp': ['permitAll'],
'/assets/**': ['permitAll'],
'/**/js/**': ['permitAll'],
'/**/css/**': ['permitAll'],
'/**/images/**': ['permitAll'],
'/**/favicon.ico': ['permitAll']
]
grails.plugin.springsecurity.rest.login.active = true
grails.plugin.springsecurity.rest.login.endpointUrl = '/api/login'
grails.plugin.springsecurity.rest.login.failureStatusCode = '401'
grails.plugin.springsecurity.rest.login.useJsonCredentials = true
grails.plugin.springsecurity.rest.login.usernamePropertyName = 'username'
grails.plugin.springsecurity.rest.login.passwordPropertyName = 'password'
grails.plugin.springsecurity.rest.logout.endpointUrl = '/api/logout'
grails.plugin.springsecurity.rest.token.generation.useSecureRandom = true
grails.plugin.springsecurity.rest.token.generation.useUUID = false
grails.plugin.springsecurity.rest.token.storage.useGorm = false
grails.plugin.springsecurity.rest.token.storage.gorm.tokenDomainClassName = null
grails.plugin.springsecurity.rest.token.storage.gorm.tokenValuePropertyName = 'tokenValue'
grails.plugin.springsecurity.rest.token.storage.gorm.usernamePropertyName = 'username'
grails.plugin.springsecurity.rest.token.rendering.usernamePropertyName = 'username'
grails.plugin.springsecurity.rest.token.rendering.authoritiesPropertyName = 'roles'
grails.plugin.springsecurity.rest.token.validation.active = true
grails.plugin.springsecurity.rest.token.validation.headerName = 'X-Auth-Token'
grails.plugin.springsecurity.rest.token.validation.endpointUrl = '/api/validate'
//Exclude normal controllers from basic auth filter. Just the JSON API is included
grails.plugin.springsecurity.filterChain.chainMap = [
'/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // Stateless chain
'/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter' // Traditional chain
]
I'm not sure what to do next. How can I to call the api? For example, I have Client domain. How to get the list of all clients with the rest api?
Client.groovy
class Client {
Long id
String name
String toString(){
"${name}"
}
static hasMany = [users: User, apps: App]
static constraints = {
name blank: false
}
}
ClientController.groovy
package m15
import static org.springframework.http.HttpStatus.*
import org.springframework.security.access.annotation.Secured
import grails.transaction.Transactional
#Secured(['IS_AUTHENTICATED_REMEMBERED'])
#Transactional(readOnly = true)
class ClientController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Client.list(params), model:[clientInstanceCount: Client.count()]
}
def show(Client clientInstance) {
respond clientInstance
}
def create() {
respond new Client(params)
}
#Transactional
def save(Client clientInstance) {
if (clientInstance == null) {
notFound()
return
}
if (clientInstance.hasErrors()) {
respond clientInstance.errors, view:'create'
return
}
clientInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'client.label', default: 'Client'), clientInstance.id])
redirect clientInstance
}
'*' { respond clientInstance, [status: CREATED] }
}
}
def edit(Client clientInstance) {
respond clientInstance
}
#Transactional
def update(Client clientInstance) {
if (clientInstance == null) {
notFound()
return
}
if (clientInstance.hasErrors()) {
respond clientInstance.errors, view:'edit'
return
}
clientInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'Client.label', default: 'Client'), clientInstance.id])
redirect clientInstance
}
'*'{ respond clientInstance, [status: OK] }
}
}
#Transactional
def delete(Client clientInstance) {
if (clientInstance == null) {
notFound()
return
}
clientInstance.delete flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'Client.label', default: 'Client'), clientInstance.id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'client.label', default: 'Client'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}
Are you able to login and get a token?
I am using Grails 3.2.0.M1 and the following settings in application.groovy
I setup a test app and use GORM to store tokens.
I first needed to install the gorm plugin otherwise the tokens would not get stored.
compile "org.grails.plugins:spring-security-rest-gorm:2.0.0.M2"
application.groovy:
grails.plugin.springsecurity.rest.login.active=true
grails.plugin.springsecurity.rest.login.endpointUrl='/api/login'
grails.plugin.springsecurity.rest.login.failureStatusCode=401
grails.plugin.springsecurity.rest.login.useJsonCredentials=true
grails.plugin.springsecurity.rest.login.usernamePropertyName='username'
grails.plugin.springsecurity.rest.login.passwordPropertyName='password'
grails.plugin.springsecurity.rest.logout.endpointUrl='/api/logout'
grails.plugin.springsecurity.rest.token.storage.useGorm=true
grails.plugin.springsecurity.rest.token.storage.gorm.tokenDomainClassName='dashboard.AuthToken'
grails.plugin.springsecurity.rest.token.storage.gorm.tokenValuePropertyName='tokenValue'
grails.plugin.springsecurity.rest.token.storage.gorm.usernamePropertyName='username'
grails.plugin.springsecurity.rest.token.generation.useSecureRandom=true
grails.plugin.springsecurity.rest.token.generation.useUUID=false
grails.plugin.springsecurity.rest.token.validation.active=true
grails.plugin.springsecurity.rest.token.validation.endpointUrl='/api/validate'
grails.plugin.springsecurity.rest.token.storage.jwt.expiration=99999
I used curl to test the login and see if a token was returned:
curl -v -H "Content-Type: application/json" -X POST -d '{"username":"me","password":"password"}' http://localhost:8090/api/login
This returned:
* Hostname was NOT found in DNS cache
* Trying ::1...
* Connected to localhost (::1) port 8090 (#0)
> POST /api/login HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8090
> Accept: */*
> Content-Type: application/json
> Content-Length: 39
>
* upload completely sent off: 39 out of 39 bytes
< HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Cache-Control: no-store
< Pragma: no-cache
< Content-Type: application/json;charset=UTF-8
< Content-Length: 112
< Date: Wed, 01 Jun 2016 09:34:00 GMT
<
* Connection #0 to host localhost left intact
{"username":"me","roles":["ROLE_ADMIN"],"token_type":"Bearer","access_token":"ek6bonq0jnvn40pbgb4qamptorfrdotb"}
The access_token is ek6bonq0jnvn40pbgb4qamptorfrdotb
Secured controller test action:
class TestController {
def index() {
def l = SysRole.list()
render l as JSON
}
}
Then I tested this secured controller using curl and the token which lists the user roles:
curl -v -X POST http://localhost:8090/test -H "Authorization: Bearer ek6bonq0jnvn40pbgb4qamptorfrdotb"
* Connected to localhost (::1) port 8090 (#0)
> POST /test HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8090
> Accept: */*
> Authorization: Bearer ek6bonq0jnvn40pbgb4qamptorfrdotb
>
< HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< X-Application-Context: application:development:8090
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Wed, 01 Jun 2016 09:39:52 GMT
<
* Connection #0 to host localhost left intact
[{"id":1,"authority":"ROLE_ADMIN"},{"id":2,"authority":"ROLE_USER"}]
Testing with an invalid token gives a 401 unauthorized:
* Connected to localhost (::1) port 8090 (#0)
> POST /test HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8090
> Accept: */*
> Authorization: Bearer invalid_token
>
< HTTP/1.1 401 Unauthorized
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< WWW-Authenticate: Bearer error="invalid_token"
< Content-Length: 0
< Date: Wed, 01 Jun 2016 09:43:08 GMT
<
* Connection #0 to host localhost left intact
Related
When queue-ing a build manually using TFS2018 the shelveset name is not showing the source branch name in all cases. Sometimes it is filled out sometimes not. Since I am picking up build variables for the source branch
$(Build.SourceBranch)
$(Build.SourceBranchName)
They will be empty if the Shelveset name is empty.
Is it possible to set the shelveset name default as the sourcebranch using the API when creating the build definition?
Is there another build variable that I can use to get the source path for the currently built solution?
UPDATE So I am trying to update the source branches using the build api. However when called I get a
{StatusCode: 405, ReasonPhrase: 'Method Not Allowed', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Pragma: no-cache
X-TFS-ProcessId:
ActivityId:
X-TFS-Session:
X-VSS-E2EID:
X-FRAME-OPTIONS: SAMEORIGIN
X-VSS-UserData: :user
Persistent-Auth: true
Lfs-Authenticate: NTLM
X-Content-Type-Options: nosniff
Cache-Control: no-cache
Date: Fri, 09 Mar 2018 14:37:16 GMT
P3P: CP="CAO DSP COR ADMa DEV CONo TELo CUR PSA PSD TAI IVDo OUR SAMi BUS DEM NAV STA UNI COM INT PHY ONL FIN PUR LOC CNT"
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 93
Allow: GET
Content-Type: application/json; charset=utf-8
Expires: -1
}}
for the following code....
internal void UpdateSourceBranches(List<BuildDefinition> defs)
{
using (var handler = new HttpClientHandler { Credentials = new NetworkCredential(tfsUser, tfsPass) })
using (var client = new HttpClient(handler))
{
try
{
client.BaseAddress = new Uri(tfsServer);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
foreach (var def in defs)
{
var buildId = def.Id;
var sourceBranch = $"$/{def.Repository.Name}/{def.Project.Name}";
var parameters = new Dictionary<string, string> { { "BuildConfiguration", "release" },
{ "BuildPlatform", "x86|x64|ARM" },
{ "system.debug", "true" }
};
var jsonParams = JsonConvert.SerializeObject(parameters);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("id", buildId.ToString()),
new KeyValuePair<string, string>("sourceBranch", sourceBranch),
new KeyValuePair<string, string>("parameters", jsonParams)
});
var response = client.PostAsync($"DefaultCollection/{def.Repository.Name}/_apis/build/builds?api-version=3.0-preview.1", content);
var s = response.Result;
}
}
catch (Exception ex)
{
}
}
}
You should use Queue a build api to set SourceBranch, for example:
POST http://TFS2018:8080/tfs/DefaultCollection/{project}/_apis/build/builds?api-version=2.0
Content-Type: application/json
{
"definition": {
"id": 47
},
"sourceBranch":"$/CeceScrum/TestCaseProject",
"parameters":"{\"BuildConfiguration\":\"release\",\"BuildPlatform\":\"any cpu\",\"system.debug\":\"false\"}"
}
If you want to choose a shelveset to build, then the api looks like below:
POST http://TFS2018:8080/tfs/DefaultCollection/{project}/_apis/build/builds?api-version=2.0
Content-Type: application/json
{
"definition": {
"id": 47
},
"sourceBranch":"ceceShelveset;domain\\username",
"parameters":"{\"BuildConfiguration\":\"release\",\"BuildPlatform\":\"any cpu\",\"system.debug\":\"false\"}"
}
I am using TFS2018 and calling the build api like this
internal void UpdateSourceBranches(List<BuildDefinition> defs)
{
using (var handler = new HttpClientHandler { Credentials = new NetworkCredential(tfsUser, tfsPass) })
using (var client = new HttpClient(handler))
{
try
{
client.BaseAddress = new Uri(tfsServer);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
foreach (var def in defs)
{
var buildId = def.Id;
var sourceBranch = $"$/{def.Repository.Name}/{def.Project.Name}";
var parameters = new Dictionary<string, string> { { "BuildConfiguration", "release" },
{ "BuildPlatform", "x86|x64|ARM" },
{ "system.debug", "true" }
};
var jsonParams = JsonConvert.SerializeObject(parameters);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("id", buildId.ToString()),
new KeyValuePair<string, string>("sourceBranch", sourceBranch),
new KeyValuePair<string, string>("parameters", jsonParams)
});
var response = client.PostAsync($"DefaultCollection/{def.Repository.Name}/_apis/build/builds?api-version=3.0-preview.1", content);
var s = response.Result;
}
}
catch (Exception ex)
{
}
}
}
But getting the following response
{StatusCode: 405, ReasonPhrase: 'Method Not Allowed', Version: 1.1,
Content: System.Net.Http.StreamContent, Headers: { Pragma: no-cache
X-TFS-ProcessId: ActivityId: X-TFS-Session: X-VSS-E2EID:
X-FRAME-OPTIONS: SAMEORIGIN X-VSS-UserData: :user Persistent-Auth:
true Lfs-Authenticate: NTLM X-Content-Type-Options: nosniff
Cache-Control: no-cache Date: Fri, 09 Mar 2018 14:37:16 GMT P3P:
CP="CAO DSP COR ADMa DEV CONo TELo CUR PSA PSD TAI IVDo OUR SAMi BUS
DEM NAV STA UNI COM INT PHY ONL FIN PUR LOC CNT" Server:
Microsoft-IIS/10.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET
Content-Length: 93 Allow: GET Content-Type: application/json;
charset=utf-8 Expires: -1 }}
Any idea why I am getting Method Not Allowed ?
Here is the bad response:
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content:
System.Net.Http.StreamContent, Headers: { Pragma: no-cache
X-TFS-ProcessId: xxxxxx-xxxxx-xxxxx-xxxxxx-xxxxxxxxxx ActivityId:
xxxxxx-xxxxx-xxxxx-xxxxxx-xxxxxxxxxx X-TFS-Session:
xxxxxx-xxxxx-xxxxx-xxxxxx-xxxxxxxxxx X-VSS-E2EID:
xxxxxx-xxxxx-xxxxx-xxxxxx-xxxxxxxxxx X-FRAME-OPTIONS: SAMEORIGIN
X-VSS-UserData: xxxxxx-xxxxx-xxxxx-xxxxxx-xxxxxxxxxx:user
Persistent-Auth: true Lfs-Authenticate: NTLM
X-Content-Type-Options: nosniff Cache-Control: no-cache Date: Tue,
13 Mar 2018 09:21:53 GMT P3P: CP="...multiple keywords" Server:
Microsoft-IIS/10.0 X-AspNet-Version: 4.0.30319 X-Powered-By:
ASP.NET Content-Length: 547 Content-Type: application/json;
charset=utf-8 Expires: -1 }}
Sounds like your POST is the issue, try using a GET
You should use api-version=2.0 instead of 3.0-preview.1.
Update code snippet:
string con = "{\"definition\": {\"id\": 47},\"sourceBranch\":\"$/CeceScrum/TestCaseProject\",\"parameters\":\"{\\\"BuildConfiguration\\\":\\\"release\\\",\\\"BuildPlatform\\\":\\\"any cpu\\\",\\\"system.debug\\\":\\\"false\\\"}\"}";
var patchValue = new StringContent(con, Encoding.UTF8, "application/json");
HttpClient httpClient = new HttpClient();
string _credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "domain\\username", "password")));
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", _credentials);
var method = new HttpMethod("POST");
var request = new HttpRequestMessage(method, "http://TFS2018:8080/tfs/DefaultCollection/CeceScrum/_apis/build/builds?api-version=2.0") { Content = patchValue };
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = httpClient.SendAsync(request).Result;
string re= response.Content.ReadAsStringAsync().Result;
I have a working Spring Boot with AngularJs app based on https://spring.io/guides/tutorials/spring-security-and-angular-js/
I am using Basic Authentication and I would like to write an integration test for it. Currently, I always get a 403 status code with the message:
Expected CSRF token not found. Has your session expired?
This is my test:
#Test
public void givenAdmin_deleteOfBookIsAllowed() {
Response response = given().auth().preemptive().basic("admin", "admin").get("/api/user/");
response.then().log().all();
String sessionId = response.sessionId();
String token = response.cookie("XSRF-TOKEN");
Book book = Books.randomBook();
bookRepository.save(book);
given()
.sessionId(sessionId)
.cookie("XSRF-TOKEN", token)
.header("X-XSRF-TOKEN", token)
.pathParam("id", book.getId().getId())
.when().delete("/api/books/{id}")
.then().statusCode(HttpStatus.SC_NO_CONTENT);
}
I am using a custom token repo per the tutorial:
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
This is the request/response from the first call:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=7020E58A8D6DC2C883FD5D6BD086512A; Path=/; HttpOnly
Set-Cookie: XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91; Path=/
X-Application-Context: application:staging:0
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Fri, 08 Jan 2016 07:43:21 GMT
{
"details": {
"remoteAddress": "127.0.0.1",
"sessionId": "7020E58A8D6DC2C883FD5D6BD086512A"
},
"authorities": [
{
"authority": "ROLE_ADMIN"
},
{
"authority": "ROLE_USER"
}
],
"authenticated": true,
"principal": {
"password": null,
"username": "admin",
"authorities": [
{
"authority": "ROLE_ADMIN"
},
{
"authority": "ROLE_USER"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "admin"
}
And from the second call:
Request method: DELETE
Request path: http://localhost:64588/api/books/04ad6d12-9b59-4ade-9a8a-e45daccb1f61
Proxy: <none>
Request params: <none>
Query params: <none>
Form params: <none>
Path params: id=04ad6d12-9b59-4ade-9a8a-e45daccb1f61
Multiparts: <none>
Headers: X-XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91
Accept=*/*
Cookies: JSESSIONID=7020E58A8D6DC2C883FD5D6BD086512A
XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91
Body: <none>
HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Fri, 08 Jan 2016 07:44:21 GMT
{
"timestamp": "2016-01-08T07:44:21.963+0000",
"status": 403,
"error": "Forbidden",
"message": "Expected CSRF token not found. Has your session expired?",
"path": "/api/books/04ad6d12-9b59-4ade-9a8a-e45daccb1f61"
}
You need to do 2 GET before post to use spring security CSRF protection in your rest client or integration test.
Make a GET request to login. This will return the JSESSIONID token and XSRF-TOKEN tokens. If you use the returned XSRF-TOKEN to POST it will fail, because we got it using empty/false JSESSIONID.
Get a useful XSRF-TOKEN from the second GET, using JSESSIONID from previous request.
Now you can use XSRF-TOKEN for your POST.
Example integration test for rest assured with basic auth and CSRF protection:
//1) get sessionId
Response response =
given().auth().preemptive().basic(userName, userPassword).contentType(JSON).
when().get(PREFIX_URL + "/user").
then().log().all().extract().response();
String jsessionidId = response.getSessionId();//or response.cookie("JSESSIONID");
//2) get XSRF-TOKEN using new/real sessionId
response =
given().
sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
contentType(JSON).
when().get(PREFIX_URL + "/user").
then().log().all().extract().response();
//3) post data using XSRF-TOKEN
given().log().all().
sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
header("X-XSRF-TOKEN", response.cookie("XSRF-TOKEN")).
queryParam("param",paramValue)).
body(someData).
contentType(JSON).
when().
post(PREFIX_URL + "/post_some_data").
then().
log().all().assertThat().statusCode(200);
I can't figure out how to get ember-simple-auth with ember-simple-auth-devise authorizer (version 0.7.2) to append the token header to my backend requests.
Here is how my config/environment.js looks:
var ENV = {
modulePrefix: 'frontend-app',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {}
},
APP: {},
'simple-auth': {
authenticationRoute: 'sign-in',
authorizer: 'simple-auth-authorizer:devise'
}
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
ENV.contentSecurityPolicy = {
'font-src': "'self' http://fonts.gstatic.com",
'style-src': "'self' 'unsafe-inline' http://fonts.googleapis.com",
'connect-src': "'self' http://localhost:3000",
};
ENV['simple-auth-devise'] = {
serverTokenEndpoint: "//localhost:3000/users/sign_in"
};
ENV.apiHost = "http://localhost:3000";
}
I was able to get authentication working, I get a session object in my application. Here are the headers from a successful exchange with the session endpoint:
POST /users/sign_in HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 63
Accept: application/json, text/javascript
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:4200/sign-in
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
And the server responds with this session object:
{"user_token":"eo2xxzYsQ6UifMKvoLMF","user_id":3,"user_email":"snip#snip.com","user_first_name":"Xavier","user_last_name":"Lange"}
But all subsequent requests don't set the header:
GET /api/reports?user_id=3 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Referer: http://localhost:4200/reports
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
If-None-Match: W/"8e1b98de900ac194a0bc3ef6e9dc7459"
Where I should expect
Authorization: Token token="<token>", email="<email>"
to be in the headers sent to the backend. But it's not happening.
The issue was solved by checking the crossOriginWhitelist in development:
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
ENV.contentSecurityPolicy = {
'font-src': "'self' http://fonts.gstatic.com",
'style-src': "'self' 'unsafe-inline' http://fonts.googleapis.com",
'connect-src': "'self' http://localhost:3000",
};
ENV['simple-auth']['crossOriginWhitelist'] = ['http://localhost:3000'];
ENV['simple-auth-devise']['serverTokenEndpoint'] = "//localhost:3000/users/sign_in"
ENV.apiHost = "http://localhost:3000";
}
Then I had to change simple-auth-devise to use email as the identification property (rails was having a hardtime parsing the Authorization header with authenticate_with_http_token:
var ENV = {
modulePrefix: 'my-app',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
'simple-auth': {
authenticationRoute: 'sign-in',
authorizer: 'simple-auth-authorizer:devise'
},
'simple-auth-devise': {
identificationAttributeName: 'email'
}
};
And my session serializer had to sync up with that attribute name:
class SessionsController < Devise::SessionsController
skip_before_action :authenticate_user_from_token!, only: [:create]
def create
respond_to do |format|
format.json do
self.resource = warden.authenticate!(auth_options)
sign_in(resource_name, resource)
data = {
token: self.resource.authentication_token,
user_id: self.resource.id,
email: self.resource.email,
user_first_name: self.resource.first_name,
user_last_name: self.resource.last_name
}
render json: data, status: 201
end
end
end
end
my application should sent structure via json,
that functions works fine:
function send_json()
{
var formData = form2object('myForm');
var json_data = JSON.stringify(formData, null, '\t');
$.post("", json_data);
}
It create json_data and send it via post.
But I don't understand how to catch this data on another side, can you help me ?
I should take this structure and make some actions with it.
when i send data i got json:
{ "type": "test", "ttl": "1", "amount": "1", "rules": { "rule1": "1", "value1": "4444", "stackcount1": "1", "rule2": "2", "value2": "333", "stackcount2": "2" }}
In the contoller i wrote this:
class AdminController < ApplicationController
def index
#json_string = params[:amount]
if !#json_sting.nil?
#json_string = JSON.parse(params[:amount])
end
end
end
in the view(haml) i wrote:
=#json_string
It doesn't work !
But wireshark shows:
POST /admin/ HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1
Accept: */*
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:3000/admin/
Content-Length: 192
Cookie: _GiftSenderTool_session=BAh7BzoQX2NzcmZfdG9rZW4iMTVyNVJCMTBvcGpKSk5OcFN4bW15YVlpRlF1TUNtb1gwZkY0bTRuRHlsNnM9Ig9zZXNzaW9uX2lkIiU4ZjFjNTNjNTY0MTQ1MGExNmFiODcxZGEyYzU5ZTkxMg%3D%3D--f21bf3b96edc3bc551c8de68acc6da17685d58b4
Pragma: no-cache
Cache-Control: no-cache
{
."type": "test",
."ttl": "1",
."amount": "1",
."rules": {
.."rule1": "1",
.."value1": "4444",
.."stackcount1": "1"
.},
."authenticity_token": "n3B6D/Km9zOgOzlosK/OLHyTCDLk3MyCTlTBinfQcnY="
}
It means that i can't get data.
you have to catch your JSON object in some rails controller action. If your JSON string looks like this:
{ "user": { "id": 1, "name": "Tom" } }
then do it like this:
require "json"
...
def some_action
user = JSON.parse(params[:user])
puts "User id is #{user[:id]}"
...
end