Skip to content

Commit

Permalink
feat: add password grant utility and fix client id and grant type (#602)
Browse files Browse the repository at this point in the history
* feat(oauth2): add password grant support

* fix(oauth2 scheme): pass `opts` to `passwordLogin`

* feat(local and refresh): properly support for client id and grant type

* demo: remove client id and grant type

* test: remove client id and grant type

* chore(deps): add dependency requrl and remove is-https

* refactor(oauth2 scheme): use requrl in `_logoutRedirectURI` method

* feat(utilities): add utility `urlJoin`

* refactor(oauth2 scheme): use utility `urlJoin` in `_logoutRedirectURI` method

* feat(provier utils): add utility `initializePasswordGrantFlow`

This remove password grant from `addAuthorize`.

* refactor(laravel passport): use `initializePasswordGrantFlow` and add url error

* fix(oauth2 scheme): use requrl and urlJoin in `_redirectURI` method

* revert: "fix(oauth2 scheme): pass `opts` to `passwordLogin`"

This reverts commit 51c7c99

* revert: "feat(oauth2): add password grant support"

This reverts commit ffc3a22

* merge with dev

* fix(provider utils): import requrl

* chore(deps): remove dependency is-https

We are using requrl

* fix(laravel passport): relative url for endpoints (#633)

Uses `assignAbsoluteEndpoints` utility

* fix(laravel passport): update `_name` and `_scheme` to remove `_`

* fix(provider utils): check if endpoint is defined in `assignAbsoluteEndpoints`

* fix(laravel passport): `assignAbsoluteEndpoints` should be used right after `assingDefaults`

* fix(laravel passport): fix export

* fix(provider utils): check if endpoint url is defined in `assignAbsoluteEndpoints`

* fix(provider utils): update `_name` to remove `_`

* chore(deps): update dependency requrl to v2.0.1

* feat(utils): add utility `cleanObj`

* fix(refresh scheme): fix data of refresh endpoint

* demo: add laravel passport demo

* fix(laravel passport): set default logout endpoint to `false`

* demo(laravel passport): remove unnecessary logout endpoint

Co-authored-by: pooya parsa <pooya@pi0.ir>
  • Loading branch information
JoaoPedroAS51 and pooya parsa committed Apr 24, 2020
1 parent fcde62e commit f47885c
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 175 deletions.
6 changes: 2 additions & 4 deletions demo/api/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ app.post('/login', (req, res) => {
res.json({
token: {
accessToken,
refreshToken,
clientId: '123'
refreshToken
}
})
})
Expand All @@ -86,8 +85,7 @@ app.post('/refresh', (req, res) => {

refreshTokens[newRefreshToken] = {
accessToken,
user,
clientId: '123'
user
}

res.json({
Expand Down
40 changes: 33 additions & 7 deletions demo/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,6 @@ export default {
property: 'token.refreshToken',
data: 'refreshToken',
maxAge: false
},
clientId: {
property: 'token.clientId',
data: 'clientId'
},
grantType: {
data: 'grantType'
}
},
auth0: {
Expand Down Expand Up @@ -98,6 +91,39 @@ export default {
laravelSanctum: {
url: '/laravel'
},
laravelPassport: {
url: 'https://laravel-auth.nuxtjs.app',
endpoints: {
userInfo: '/api/auth/passport/user'
},
token: {
maxAge: 1800
},
refreshToken: {
maxAge: 60 * 60 * 24 * 30
},
clientId: '3',
clientSecret: 'k0NAhYGKXbG6NjENFz4VIe5YSbccZWW9V3gGeSOa'
},
laravelPassportPasswordGrant: {
name: 'laravel.passport.password',
provider: 'laravelPassport',
url: '/laravel',
endpoints: {
user: {
url: '/api/auth/passport/user'
}
},
token: {
maxAge: 1800
},
refreshToken: {
maxAge: 60 * 60 * 24 * 30
},
clientId: '2',
clientSecret: 'eKm1ei8muaql7TfcBxhN6Nq48oSflw6QJKCZF8gl',
grantType: 'password'
},
oauth2mock: {
scheme: 'oauth2',
endpoints: {
Expand Down
45 changes: 45 additions & 0 deletions demo/pages/login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,26 @@
Login with Laravel JWT (Test User)
</b-btn>
</div>
<div class="mb-2">
<b-btn
block
:style="{background: '#c61952'}"
class="login-button"
@click="loginPassport"
>
Login with Laravel Passport (Test User)
</b-btn>
</div>
<div class="mb-2">
<b-btn
block
:style="{background: '#8c0d32'}"
class="login-button"
@click="loginPassportGrantFlow"
>
Login with Laravel Passport Password Grant (Test User)
</b-btn>
</div>
</b-card>
</b-col>
</b-row>
Expand Down Expand Up @@ -189,6 +209,31 @@ export default {
})
},
async loginPassport () {
this.error = null
return this.$auth
.loginWith('laravelPassport')
.catch((e) => {
this.error = e.response ? e.response.data : e.toString()
})
},
async loginPassportGrantFlow () {
this.error = null
return this.$auth
.loginWith('laravel.passport.password', {
data: {
username: 'test@test.com',
password: '12345678'
}
})
.catch((e) => {
this.error = e.response ? e.response.data : e.toString()
})
},
async loginSanctum () {
this.error = null
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
"consola": "^2.11.3",
"cookie": "^0.4.1",
"defu": "^2.0.2",
"is-https": "^1.0.0",
"js-cookie": "^2.2.1",
"jwt-decode": "^2.2.0",
"lodash": "^4.17.15",
"nanoid": "^3.1.3"
"nanoid": "^3.1.3",
"requrl": "^2.0.1"
},
"devDependencies": {
"@nuxt/types": "latest",
Expand Down
92 changes: 82 additions & 10 deletions src/providers/_utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import defu from 'defu'
import axios from 'axios'
import bodyParser from 'body-parser'
import requrl from 'requrl'

export function assignDefaults (strategy, defaults) {
Object.assign(strategy, defu(strategy, defaults))
Expand All @@ -17,7 +18,7 @@ export function addAuthorize (nuxt, strategy) {
delete strategy.clientSecret

// Endpoint
const endpoint = `/_auth/oauth/${strategy._name}/authorize`
const endpoint = `/_auth/oauth/${strategy.name}/authorize`
strategy.endpoints.token = endpoint

// Set response_type to code
Expand Down Expand Up @@ -80,23 +81,94 @@ export function addAuthorize (nuxt, strategy) {
})
}

export function initializePasswordGrantFlow (nuxt, strategy) {
// Get clientSecret, clientId, endpoints.login.url
const clientSecret = strategy.clientSecret
const clientId = strategy.clientId
const tokenEndpoint = strategy.endpoints.token

// IMPORTANT: remove clientSecret from generated bundle
delete strategy.clientSecret

// Endpoint
const endpoint = `/_auth/${strategy.name}/token`
strategy.endpoints.login.url = endpoint
strategy.endpoints.refresh.url = endpoint

// Form data parser
const formMiddleware = bodyParser.json()

// Register endpoint
nuxt.options.serverMiddleware.unshift({
path: endpoint,
handler: (req, res, next) => {
if (req.method !== 'POST') {
return next()
}

formMiddleware(req, res, () => {
const {
username,
password,
grant_type: grantType = strategy.grantType,
refresh_token: refreshToken
} = req.body

// Grant type is password, but username or password is not available
if (grantType === 'password' && (!username || !password)) {
return next(new Error('Invalid username or password'))
}

// Grant type is refresh token, but refresh token is not available
if (grantType === 'refresh_token' && !refreshToken) {
return next(new Error('Refresh token not provided'))
}

axios
.request({
method: 'post',
url: tokenEndpoint,
baseURL: requrl(req),
data: {
client_id: clientId,
client_secret: clientSecret,
refresh_token: refreshToken,
grant_type: grantType,
username,
password
},
headers: {
Accept: 'application/json'
}
})
.then((response) => {
res.end(JSON.stringify(response.data))
})
.catch(error => next(error))
})
}
})
}

export function assignAbsoluteEndpoints (strategy) {
const { url, endpoints } = strategy

if (endpoints) {
for (const key of Object.keys(endpoints)) {
const endpoint = endpoints[key]

if (typeof endpoint === 'object') {
if (endpoint.url.startsWith(url)) {
continue
}
endpoints[key].url = url + endpoint.url
} else {
if (endpoint.startsWith(url)) {
continue
if (endpoint) {
if (typeof endpoint === 'object') {
if (!endpoint.url || endpoint.url.startsWith(url)) {
continue
}
endpoints[key].url = url + endpoint.url
} else {
if (endpoint.startsWith(url)) {
continue
}
endpoints[key] = url + endpoint
}
endpoints[key] = url + endpoint
}
}
}
Expand Down
62 changes: 48 additions & 14 deletions src/providers/laravel.passport.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { assignDefaults, addAuthorize, assignAbsoluteEndpoints } from './_utils'
import { assignDefaults, addAuthorize, initializePasswordGrantFlow, assignAbsoluteEndpoints } from './_utils'

export function laravelPassport (nuxt, strategy) {
const { url } = strategy
const { url, grantType } = strategy
const isPasswordGrant = grantType === 'password'

if (!url) {
throw new Error('url is required is laravel passport!')
}

assignDefaults(strategy, {
scheme: 'oauth2',
endpoints: {
authorization: url + '/oauth/authorize',
token: url + '/oauth/token'
},
const defaults = {
name: 'laravel.passport',
token: {
property: 'access_token',
type: 'Bearer',
Expand All @@ -21,13 +18,50 @@ export function laravelPassport (nuxt, strategy) {
},
refreshToken: {
property: 'refresh_token',
data: 'refresh_token',
maxAge: 60 * 60 * 24 * 30
},
responseType: 'code',
grantType: 'authorization_code',
scope: '*'
})
user: {
property: false
}
}

if (isPasswordGrant) {
assignDefaults(strategy, {
...defaults,
scheme: 'refresh',
endpoints: {
token: url + '/oauth/token',
login: {},
refresh: {},
logout: {
url: false
},
user: {
url: url + '/api/auth/user'
}
},
grantType: 'password'
})

assignAbsoluteEndpoints(strategy)
addAuthorize(nuxt, strategy)
assignAbsoluteEndpoints(strategy)
initializePasswordGrantFlow(nuxt, strategy)
} else {
assignDefaults(strategy, {
...defaults,
scheme: 'oauth2',
endpoints: {
authorization: url + '/oauth/authorize',
token: url + '/oauth/token',
userInfo: url + '/api/auth/user',
logout: false
},
responseType: 'code',
grantType: 'authorization_code',
scope: '*'
})

assignAbsoluteEndpoints(strategy)
addAuthorize(nuxt, strategy)
}
}
Loading

0 comments on commit f47885c

Please sign in to comment.