Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for CSRF token in a child element #44

Merged
merged 5 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ import '@github/auto-check-element'
</auto-check>
```

Note that in the following example the CSRF element is marked with the `data-csrf` attribute rather than `name` so that the value doesn't get posted to the backend when the element is placed in a form.

```erb
koddsson marked this conversation as resolved.
Show resolved Hide resolved
<auto-check src="/signup-check/username">
<input>
<input hidden data-csrf value="<%= authenticity_token_for("/signup-check/username") %>">
koddsson marked this conversation as resolved.
Show resolved Hide resolved
</auto-check>
```

## Attributes

- `src` is the server endpoint that will receive POST requests. The posted form contains a `value` parameter containing the text input to validate. Responding with a 200 OK status indicates the provided value is valid. Any other error status response indicates the provided value is invalid.
- `csrf` is the [CSRF][] token for the posted form. It's available in the request body as a `authenticity_token` form parameter.
- You can also supply the CSRF token via a child element. See [usage](#Usage) example.
- `required` is a boolean attribute that requires the validation to succeed before the surrounding form may be submitted.

## Events
Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ export default class AutoCheckElement extends HTMLElement {
}

get csrf(): string {
return this.getAttribute('csrf') || ''
const csrfElement = this.querySelector('[data-csrf]')
return this.getAttribute('csrf') || (csrfElement instanceof HTMLInputElement && csrfElement.value) || ''
}

set csrf(value: string) {
Expand Down
29 changes: 29 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,35 @@ describe('auto-check element', function() {
assert.deepEqual(events, [])
})
})

describe('csrf support', function() {
afterEach(function() {
document.body.innerHTML = ''
})

it('fetches CSRF tokens from attributes', function() {
const container = document.createElement('div')
container.innerHTML = `
<auto-check csrf="foo" src="/success" required>
<input>
</auto-check>`
document.body.append(container)
const autoCheck = document.querySelector('auto-check')
assert.equal(autoCheck.csrf, 'foo')
})

it('fetches CSRF tokens from child elements', function() {
const container = document.createElement('div')
container.innerHTML = `
<auto-check src="/success" required>
<input>
<input type="hidden" data-csrf value="foo">
</auto-check>`
document.body.append(container)
const autoCheck = document.querySelector('auto-check')
assert.equal(autoCheck.csrf, 'foo')
})
})
})

function once(element, eventName) {
Expand Down