Skip to content

Instantly share code, notes, and snippets.

@stctheproducer
Last active July 6, 2020 19:07
Show Gist options
  • Save stctheproducer/f312c1f4a3c7e7a0c8953eb9563bee62 to your computer and use it in GitHub Desktop.
Save stctheproducer/f312c1f4a3c7e7a0c8953eb9563bee62 to your computer and use it in GitHub Desktop.
I am getting an error in which my auth session is saved in redis but doesn't seem to be propagated to rest of the application. I am using FusionAuth as my authentication server. Below are my files including their configurations.
authenticator: 'session',
/*
|--------------------------------------------------------------------------
| Session
|--------------------------------------------------------------------------
|
| Session authenticator makes use of sessions to authenticate a user.
| Session authentication is always persistent.
|
*/
session: {
serializer: 'lucid',
model: 'App/Models/User',
scheme: 'session',
uid: 'email',
password: 'password',
},
async authorize({ request, response }) {
const { response_type } = request.get()
try {
// Check if OAuth flow is Authorization Code Grant Request
if (response_type !== Env.get('OAUTH2_FLOW_TYPE')) {
response.badRequest()
}
// Redirect to FusionAuth
response.redirect(
`${Env.get('FUSIONAUTH_URL')}/oauth2/authorize`,
true,
307
)
} catch ({ message, stack }) {
Logger.error('%j', { message, stack })
}
}
// Handle token exchange
async token({ request, response }) {
const { code, redirect_uri, client_id } = request.post()
// Check for code in request
if (!code) {
throw new Error('An authorization code was not received')
}
// Exchange authorization code for an access token
const { data } = await axios({
url: `${Env.get('FUSIONAUTH_URL')}/oauth2/token`,
method: 'POST',
data: stringify({
client_id,
client_secret: Env.get('FUSIONAUTH_CLIENT_SECRET'),
code,
grant_type: 'authorization_code',
redirect_uri,
}),
headers,
})
// Logger.info('Login data: %j', data)
response.send(data)
}
// Handle user information
async info({ request, response, auth, session }) {
const token = request.header('authorization')
// Logger.info('Current Token: %s', token)
try {
// Check for token
if (token) {
// Introspect token
const { data: introspectResponse } = await axios({
url: `${Env.get('FUSIONAUTH_URL')}/oauth2/introspect`,
method: 'post',
data: stringify({
client_id: Env.get('FUSIONAUTH_CLIENT_ID'),
token: token.substr(7),
}),
headers,
})
// Check if token is still active
if (introspectResponse.active) {
const { data: registration } = await axios({
url: `${Env.get('FUSIONAUTH_URL')}/api/user/registration/${
introspectResponse.sub
}/${Env.get('FUSIONAUTH_CLIENT_ID')}`,
method: 'get',
headers: {
Authorization: Env.get('FUSIONAUTH_API_KEY'),
},
})
const { data } = await axios({
url: `${Env.get('FUSIONAUTH_URL')}/oauth2/userinfo`,
method: 'get',
headers: {
Authorization: token,
},
})
// Logger.info('UserInfo: %j', data)
const userData = Object.assign(introspectResponse, data, registration)
// Standardize phone numbers
let phoneNo = undefined
if (userData.phone_number) {
// If the phone number isn't prefixed
if (!userData.phone_number.startsWith('+')) {
// if the phone number starts with '0'
if (userData.phone_number.startsWith('0')) {
phoneNo = '+26' + userData.phone_number
} else {
phoneNo = '+' + userData.phone_number
}
} else {
phoneNo = userData.phone_number
}
}
// User details to be saved
const userDetails = {
username: userData.preferred_username,
first_name: userData.given_name,
middle_name: userData.middle_name ? userData.middle_name : null,
last_name: userData.family_name,
phone_number: phoneNo,
email: userData.email,
email_verified: userData.email_verified,
}
// search for existing user
const whereClause = {
email: userData.email,
}
const user = await User.findOrCreate(whereClause, userDetails)
// Logger.info('Found user: %j', user)
// Get user roles
const Role = use('Adonis/Acl/Role')
const roleAdmin = await Role.findBy('slug', 'admin')
const roleManager = await Role.findBy('slug', 'manager')
const roleUser = await Role.findBy('slug', 'user')
const roles = userData.roles
// Get roles to sync with user
const syncRoles = roles.map((role) => {
switch (role) {
case 'admin':
return roleAdmin.id
case 'manager':
return roleManager.id
default:
return roleUser.id
}
})
// Attach roles to user and login
await user.roles().sync(syncRoles)
await auth.loginViaId(user.id)
// Logger.info('Logged in: %j', loggedIn)
Logger.info('Logged in user with ID: %i', user.id)
// Logger.info('userData: %j', userData)
response.send(userData)
} else {
response.unauthorized('Access token expired')
}
} else {
Logger.error('Unauthorized: No token detected')
response.unauthorized('Not logged in!')
}
// Logger.info('Session store: %j', await session.all())
} catch ({ message, stack }) {
Logger.error('%j', { message, stack })
}
}
// Handle logout for fusionauth
async logout({ auth, response, session }) {
await auth.logout()
response.redirect(
`${Env.get('FUSIONAUTH_URL')}/oauth2/logout?client_id=${Env.get(
'FUSIONAUTH_CLIENT_ID'
)}&tenant_id=${Env.get('FUSIONAUTH_TENANT_ID')}`
)
}
auth: {
// Options
plugins: ['~/plugins/auth.js'],
defaultStrategy: 'fusionauth',
cookie: {
prefix: 'auth.',
options: {
path: '/',
secure: false
}
},
scopeKey: 'roles',
redirect: {
login: '/',
logout: '/',
callback: '/',
home: '/home'
},
strategies: {
// FusionAuth via Adonis
fusionauth: {
_scheme: 'oauth2',
authorization_endpoint: `${process.env.API_URL}/user/authorize`,
userinfo_endpoint: `${process.env.API_URL}/user/info`,
scope: ['offline_access'],
access_type: 'offline',
access_token_endpoint: `${process.env.API_URL}/user/token`,
response_type: 'code',
token_type: 'Bearer',
client_id: `${process.env.FUSIONAUTH_CLIENT_ID}`,
token_key: 'access_token',
refresh_token_key: 'refresh_token',
state: crypto.randomBytes(16).toString('hex')
}
}
}
Route.get('session-data', async ({ request, response, auth, session }) => {
try {
// let user = await auth.getUser()
const check = await auth.check()
Logger.info('Session: %j', session.all())
response.json(check)
} catch ({ message }) {
Logger.error(message)
}
})
// Response
// error: E_INVALID_SESSION: Invalid session
'use strict'
const Env = use('Env')
module.exports = {
/*
|--------------------------------------------------------------------------
| Session Driver
|--------------------------------------------------------------------------
|
| The session driver to be used for storing session values. It can be
| cookie, file or redis.
|
| For `redis` driver, make sure to install and register `@adonisjs/redis`
|
*/
driver: Env.get('SESSION_DRIVER', 'cookie'),
/*
|--------------------------------------------------------------------------
| Cookie Name
|--------------------------------------------------------------------------
|
| The name of the cookie to be used for saving session id. Session ids
| are signed and encrypted.
|
*/
cookieName: 'adonis-session',
/*
|--------------------------------------------------------------------------
| Clear session when browser closes
|--------------------------------------------------------------------------
|
| If this value is true, the session cookie will be temporary and will be
| removed when browser closes.
|
*/
clearWithBrowser: true,
/*
|--------------------------------------------------------------------------
| Session age
|--------------------------------------------------------------------------
|
| This value is only used when `clearWithBrowser` is set to false. The
| age must be a valid https://npmjs.org/package/ms string or should
| be in milliseconds.
|
| Valid values are:
| '2h', '10d', '5y', '2.5 hrs'
|
*/
age: '2h',
/*
|--------------------------------------------------------------------------
| Cookie options
|--------------------------------------------------------------------------
|
| Cookie options defines the options to be used for setting up session
| cookie
|
*/
cookie: {
httpOnly: true,
sameSite: 'none',
path: '/',
},
/*
|--------------------------------------------------------------------------
| Sessions location
|--------------------------------------------------------------------------
|
| If driver is set to file, we need to define the relative location from
| the temporary path or absolute url to any location.
|
*/
file: {
location: 'sessions',
},
/*
|--------------------------------------------------------------------------
| Redis config
|--------------------------------------------------------------------------
|
| The configuration for the redis driver.
|
*/
redis: 'self::redis.local',
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment