O LTI é um padrão para plataformas de ensino que normaliza a integração entre ferramentas e módulos educacionais (Learning Tools). A iônica 2.0 utiliza a versão 1.3 do LTI.
De modo simplista, o LTI pode ser resumido em:
Uma camada adicional ao OIDC que incorpora dados de profile do usuário (estudante, professor(a), etc.), para que o parceiro que integre via LTI consiga todos os dados de usuário necessários para a integração entre sistemas de ensino (LMS, Roles, nome, ID, etc.) e nada mais que isso. Dessa forma, além de padronizar os dados enviados, é assegurado que dados sensíveis (RG, CPF, etc.) não serão trafegados pela integração.
A interface de LTI Launch adiciona uma camada que valida se o usuário possui licença para utilizar o serviço que está tentando acessar e gera um token específico.
Fluxo de Login via IT Launch
O processo se inicia a partir da URL de LTI Launch, que executa o seguinte fluxo:
Passo 1: usuário ativa a URL de LTI via menu
A URL de LTI Launch será provisionada no Menu Global, que incorpora as páginas da iônica e as páginas dos parceiros. Essa URL aponta diretamente para um endpoint do parceiro que é responsável pelo início do fluxo de LTI Launch (explicado na documentação de integração do parceiro e do READ.MD do projeto modelo para parceiro LTI) e deve possuir o query parameter iss com o backend da iônica e o query parameter redirect_uri que aponta para a home logada do parceiro.
Para mais detalhes clica aqui.
Passo 2: parceiro recebe a requisição de início do LTI Launch
O parceiro receberá a requisição e deverá fazer as seguintes validações:
Validar a presença dos parâmetros “redirect_uri” e “iss”;
Validar se o emissor (iss) está associado com um emissor LTI válido (na própria base);
Gerar e associar o nonce para identificar a requisição LTI Launch. Esse nonce pode ser algo arbitrário à escolha do parceiro ou pode ser um UUID-v4;
Responder com uma chamada de redirecionamento (código http 307) com o location contendo a URL para o LTI com os dados de nonce, seu próprio código de cliente LTI e o redirect_uri
Passo 3: redirecionamento envia chamada para o /authorize do LTI Launch da iônica
Nesse momento é feita a validação do usuário e eventual login (redirecionando para o fluxo de autenticação SSO) se ele não estiver logado. Após o login do usuário, sua licença é revalidada para o endpoint do LTI Launch e é gerado o token LTI. Esse token é incorporado ao redirect_uri fornecido via parâmetro na URL e o redirecionamento ocorre.
Passo 4: o parceiro recebe a chamada do redirect_uri com o token LTI
A chamada para a home logada do parceiro possui dois parâmetros de query importantes: idToken e acessToken é o JWT do LTI, que possui os dados relevantes do usuário. O acessToken possui uma chave para uso das APIS de backend da iônica em nome do usuário. Nesse momento, o parceiro deve obter a chave JWKS e validar o token LTI recebido. Após a validação, deve-se criar uma sessão válida a partir dos dados recebidos do LTI e abrir a home logada para o usuário.
Conteúdo do payload do token de LTI Launch
Após completar o fluxo de LTI, a redirect_uri receberá um queryParam idToken. Ao abrir esse token JWT, o payload possuirá a estrutura abaixo:
{
"iss": "https://souionica.com.br", // domínio ionica
"sub": "a6d5c443-1f51-4783-ba1a-7686ffe3b54a", // crmId: uid do usuario logado na ionica
"aud": [
"962fa4d8-bcbf-49a0-94b2-2de05ad274af"
], // (client-id) do parceiro
"exp": 1510185728, // expiração do token, utiliza a expiração do token do adb2c
"iat": 1510185228, // data de criação do token
"jti": "1E64590C09184E4A91BD4C225EBDB242", // Identificador único da requisição que gerou o token LTI
"azp": "962fa4d8-bcbf-49a0-94b2-2de05ad274af", // (client-id) do parceiro
"nonce": "fc5fdc6d-5dd6-47f4-b2c9-5d1216e9b771", // caso o parceiro informe no request, ecoar o valor
"name": "Ms Jane Marie Doe", // nome completo do usuário na ionica
"given_name": "Jane", // primeiro nome do usuario
"family_name": "Doe", //sobrenome usuário
"picture": "https://platform.example.edu/jane.jpg", // url do avatar do usuário
"email": "[email protected]", // email do usuário
"locale": "en-US", // (pt-BR) localidade do idioma do usuário
"https://purl.imsglobal.org/spec/lti/claim/deployment_id":
// (deployment_id) campo fixo parametrizado ao parceiro
"07940580-b309-415e-a37c-914d387c1150",
"https://purl.imsglobal.org/spec/lti/claim/message_type":
// fixo tipo de mensagem LTI
"LtiResourceLinkRequest",
"https://purl.imsglobal.org/spec/lti/claim/version": "1.3.0", // valor fixo versão do LTI 1.3.0
"https://purl.imsglobal.org/spec/lti/claim/roles": [
// roles do usuário na iônica no padrão LTI
"http://purl.imsglobal.org/vocab/lis/v2/institution/person#Student",
"http://purl.imsglobal.org/vocab/lis/v2/membership#Learner",
"http://purl.imsglobal.org/vocab/lis/v2/membership#Mentor"
],
"https://purl.imsglobal.org/spec/lti/claim/context": {
// contexto de ensalamento do usuário na ionica
"id": "c1d887f0-a1a3-4bca-ae25-c375edcc131a", // guid do curso no LMS
"label": "ECON 1010", // código do curso no LMS
"title": "Economics as a Social Science", // título do curso
// tipo de contexto
"type": [
"http://purl.imsglobal.org/vocab/lis/v2/course#CourseOffering"
]
},
"https://purl.imsglobal.org/spec/lti/claim/resource_link": {
// dados do deep link
"id": "200d101f-2c14-434a-a0f3-57c2a42369fd", // identificador do link no sistema (content.guid)
},
"https://purl.imsglobal.org/spec/lti/claim/target_link_uri":
// target_url. (https://ftd.parceiro.com.br/home)
"https://tool.example.com/lti/48320/ruix8782rs",
"https://purl.imsglobal.org/spec/lti/claim/launch_presentation": {
// contexto de apresentação do parceiro
// se é iframe ou window (menu global trabalhará sempre com window)
"document_target": "window"
},
"https://purl.imsglobal.org/spec/lti/claim/custom": {
// parâmetros adicionais do deep link, customizado
"oneClickUserId": "3998975e-3e08-11eb-8122-239050e66762"
},
"https://souionica.com.br/licence": {
// claim customizado para informar o id da licenca
"id": "a78196e9-4161-46b1-9164-de4bf1280efd"
}
}
Para que o LTI Launch funcione corretamente, o parceiro deverá prover 2 endpoints específicos:
A request de login disparará o início da autenticação para o LTI.
Endpoint: GET {{partner}}/{{path_to_lti_launch_starter}}?origin={{origin}}&iss={{iss}}&redirect_uri={{redirectURL}}
Onde:
origin: tera o valor de "MenuGlobal" ou "LoginSocial";
partner: host do parceiro;
path_to_lti_launch_starter: um path arbitrário para a funcionalidade (deverá ser fornecido para a FTD durante o cadastro);
iss: emissor da requisição. Será um domínio da FTD;
redirect_url: endereço de destino. É para onde o usuário será redirecionado após o login. Deverá ser um endereço válido do parceiro.
Request
Cada solicitação deve gerar um nonce único, é recomendado um uuid-v4.
Opcionalmente, pode-se gerar um state com algum dado que o parceiro queira receber de volta na redirect_uri.
Deve-se validar se a request.iss está cadastrada e válida. Caso não esteja, retornar Unauthorized e fazer um log detalhado.
Opcionalmente, pode-se validar se a redirectURL pode ser um path válido para redirecionamento e caso não seja, retornar Forbidden e fazer um log detalhado.
Response
307 {{PartnerDB.issuers[request.iss].oidcAuthorizeUrl}}?client_id={{PartnerDB.clientId}}&redirect_url={{request.redirect_uri}}&scope=openid%20code&state={{state}}&nonce={{nonce}}&origin={{origin}}&is_app={{is_app}}
Note que o retorno do serviço é um redirect temporário.
Formato da URL e parâmetros
GET {{partner}}/{{path_to_lti_launch_starter}}?iss={{iss}}&redirect_url={{redirectURL}}&origin={{origin}}
partner: host do parceiro
path_to_lti_launch_starter: um path arbitrário para a funcionalidade (deverá ser fornecido para a FTD durante o cadastro)
iss: Emissor (issuer) da requisição. Consultar dados da iônica.
rediredt_url (B): endereço de destino. É para onde o usuário será redirecionado após login. Deverá ser um endereço válido do parceiro.
origin: repassar a origin recebida anteriormente ("MenuGlobal"), ou caso o login tenha sido iniciado pelo login-social, informar "LoginSocial".
Resposta
Deve ser um redirect temporário (307) no seguinte formato
{{oidcAuthorizeUrl}}?client_id={{clientId}}&redirect_url={{redirect_url}}&scope=openid%20code&state={{state}}&nonce={{nonce}}&origin={{origin}}&is_app={{is_app}}
oidcAuthorizeUrl: Consultar no tópico dados da iônica
clientId: Informado pela FTD após o cadastro do parceiro
redirect_url: URL para a qual o parceiro deseja que o usuário seja direcionado após o Login.
origin: deverá ser "MenuGlobal" ou "LoginSocial"
is_app: Informar "1" se for um aplicativo, ou "0" caso contrário. Obs: Se for um celular acessando o site do Parceiro, este valor deve ser "0".
A request de redirect deverá obter o token, validá-lo, criar uma sessão em seu próprio servidor utilizando os parâmetros recebidos pelo token LTI e exibir a página para o usuário.
Endpoint: GET {{partner}}/ionica/home?id_token={{id_token}}&code={{code}}&state={{state}}&nonce={{nonce}}
Onde:
partner: host do parceiro;
id_token: token LTI;
state: conterá o mesmo conteúdo preenchido na resposta da url de Login LTI Launch;
nonce: conterá o mesmo conteúdo preenchido na resposta da url de Login LTI Launch.
O parceiro deve validar o request.id_token utilizando a chave pública que é obtida via PartnerDB.jwksUrl.
Se o token for inválido, retornar erro Unauthorized, sem detalhes, e fazer um log detalhado do problema.
Todas as informações do usuário estão em request.id_token.payload, que contém os dados de LTI.
Caso request.code esteja preenchido, é possível fazer requisições nas APIs da iônica em nome do usuário.
Response
200 com HTML da página inicial
Para facilitar a integração, foi criado o projeto modelo que pode ser utilizado inicialmente para testar a configuração com a FTD e como referência para implementar a solução final com o parceiro. Para mais detalhes clica aqui.
Deep links são links que direcionam os usuários para páginas específicas dentro de um ambiente, em vez de apenas abrir na tela inicial. Com isso é possível ter um link na iônica diretamente para um recurso ou página do parceiro.
Pré-requisito
Ambiente do parceiro deve estar preparado para acessar um conteúdo específico através de uma URL
Implementação
Se o usuário possuir uma sessão ativa no Parceiro, apenas exibir o recurso/página.
Caso o usuário não possua sessão ativa, será necessário guardar o link do recurso/página que o usuário tentou acessar e na sequência iniciar o fluxo de SSO com a Iônica. Ao término desse fluxo, redirecionar o usuário para o link salvo anteriormente.