# Nest Integration
Follow this guide if you're integrating @211la/cs-sso-client
into a Nest application.
# Prepare your app
SSO Client uses sessions to identify users. For that reason you'll need to setup express-session
in your application.
$ npm i express-session
$ npm i -D @types/express-session
Then, open your main.ts
file (or the file you're bootstrapping Nest.js from), and modify it:
// main.ts
// ...
import * as session from 'express-session';
import sessionConfig from './config/session.config';
// ...
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.use(session(app.get(sessionConfig.KEY)));
// ...
Your session.config.ts
file can look like this for now:
// config/session.config.ts
import { registerAs } from '@nestjs/config';
import session from 'express-session';
export default registerAs(
'session',
(): session.SessionOptions => ({
secret: process.env.SESSION_SECRET || 'secret',
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
// This must be TRUE in production.
// If node.js is running behind a proxy, "trust proxy" must be set in express.
secure: process.env.SECURE_COOKIES === 'true',
},
// Consider using a reliable session store, such as redis...
// The default session store (in-memory) is NOT RECOMMENDED for production.
// store: new RedisStore({
// client: redis.createClient(
// +process.env.REDIS_PORT || 6379,
// process.env.REDIS_HOST || 'localhost',
// {
// password: process.env.REDIS_PASSWORD,
// },
// ),
// }),
}),
);
WARNING
🚨 This is not a production ready configuration. The default session store (MemoryStore) will leak memory and won't scale beyond a single node process. You need to use a different session store. For a list of stores, see compatible session stores (opens new window).
Further reading:
# Register the Module
Open your app.module.ts
and register the SsoClientModule
. You can pass any client configuration inside .forRoot({})
// app.module.ts
import { SsoClientModule } from ' @211la/cs-sso-client';
// ..
@Module({
imports: [
//
SsoClientModule.forRoot({
// Be sure to provide the correct app definition id defined in
// 211CS Admin. You can omit this if you are using 211CS Resolution
// Service.
appDefinitionId: 'contact',
}),
//
],
})
export class AppModule extends NestModule {}
The following routes will now be available on your Nest application:
/sso/login
Redirects the user to CS SSO login flow./sso/logout
Terminates the current SSO session./sso/callback
Handles the callback from SSO service and starts a session in your app.
In addition, you'll have access to the SsoClientService
at response.locals.sso
. You can access this service to perform authentication related tasks for more advanced operations.
# Protect Endpoints (Guard)
If you want to prevent unathenticated requests to certain endpoints, simply use the SsoAuthGuard
to get the job done.
import { SsoAuthGuard } from '@211la/cs-sso-client';
@Controller('protected')
export class ProtectedController {
@Get()
@UseGuards(SsoAuthGuard)
public index() {
// User is authenticated!
}
}
# Access Current User
Another common requirement is to access the currently authenticated user (Nexus profile). This is possible with the @CurrentUser()
decorator.
import { SsoAuthGuard, CurrentUser, NexusProfile } from '@211la/cs-sso-client';
@Controller('protected')
export class ProtectedController {
@Get()
@UseGuards(SsoAuthGuard)
public index(@CurrentUser() user: NexusProfile) {
// User is authenticated!
return user;
}
}
# Verify Session
Once the SSO handshake is complete, your app will have a local session.
If the user signs out of Caresuite from a different service or if the Nexus211 Access token expires, the local session will still be valid for your app until it expires. Therefore, you need to verify that the session is still active periodically.
Server-side rendered apps should run this check on every page load, and for Single page applications can run this check while fetching the current user.
@Get('me')
@UseGuards(SsoAuthGuard)
public async me(@CurrentUser() user: NexusProfile) {
const sessionAlive = await user.verifySession();
if (!sessionAlive) {
throw new HttpException('Unauthorized', 401);
}
return {
id: user.id,
name: user.name,
email: user.email,
organization: user.organization,
};
}
# Testing
While testing guarded endpoints in your application, you may want to sign in as a certain profile. You can do that by overriding the SsoClientService
in your test module.
import { SsoClientModule } from '@211la/cs-sso-client';
describe('My Protected Route', () => {
// Our mock user getter
let app: NestExpressApplication;
let getUser = jest.fn();
beforeAll(async () => {
const module = await Test.createTestingModule({
controllers: [TestController],
imports: [
// Be sure to register the SSO module.
SsoClientModule.forRoot({
appDefinitionId: 'test',
}),
],
})
// Then, we'll override SsoClientService
.overrideProvider(SsoClientService)
// And inject our mock user getter...
.useValue(SsoClientService.useFactory(getUser))
.compile();
app = module.createNestApplication<NestExpressApplication>();
await app.init();
});
it('returns user object', async () => {
// Now, when SSO client checks the user,
// it'll invoke this mock implementation.
getUser.mockImplementation(() => ({
id: 'testUserId',
organization: {
id: 'orgId',
name: 'org name',
},
name: 'John Doe',
email: 'johndoe@example.com',
roles: ['admin', 'member'],
}));
// If you want to go back to being a guest, you can use this:
getUser.mockImplementation(() => null);
// The rest of your test...
});
});
← Configuration Koa →