
Security News
RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.
@betha-plataforma/oauth
Advanced tools
Biblioteca JavaScript para lidar com o fluxo do OAuth 2.0 em aplicações Web, com suporte a TypeScript.
Biblioteca JavaScript para lidar com o fluxo do OAuth 2.0 em aplicações Web, com suporte a TypeScript.
Atualmente o fluxo suportado é o Implicit, abaixo temos um exemplo de como configurá-lo.
Este fluxo é usado para aplicativos móveis e aplicações web, onde a capacidade de armazenar segredos no cliente não é garantida. Entenda melhor sobre este fluxo neste guia Introdução ao OAuth 2 da Digital Ocean e caso queira entender na prática acesse o OAuth 2.0 Playground.
Após instalar, algumas configurações são necessárias.
Para realizar as configurações é necessário saber os hosts de alguns serviços, como OAuth, Login e Usuários.
Estes valores podem ser obtidos conforme constam em https://suite.cloud.betha.com.br/env.js
Entretanto sugerimos a importação dessa URL como um script no index.html para que a aplicação possa obter os valores de forma dinâmica por meio da variável window['___bth'].envs. Assim caso em algum momento seja necessário alteração nestes valores, eles serão refletidos automaticamente.
<!DOCTYPE html>
<html>
<head>
<title>OAuth Playground</title>
</head>
<body>
<!-- ... HTML ... -->
<!-- Variáveis do Env.js -->
<script src="https://suite.cloud.betha.com.br/env.js"></script>
<!-- App Scripts -->
</body>
</html>
No playground/webpack.config.js é definido uma variável dinamica chamada "envjs" a qual irá preencher a URL nos templates playground/src/index.html e playground/src/auth/callback/callback.html
Com as variáveis dos serviços em mãos, basta criar uma configuração de provedor OAuth.
const OAUTH_URL = window["___bth"].envs.suite.oauth.v1.host;
const SERVICE_LOGIN_URL = window["___bth"].envs.suite["service-login"].v1.host;
const USERS_URL = window["___bth"].envs.suite.usuarios.v1.host;
const provider = {
authorization_endpoint: `${OAUTH_URL}/authorize`,
check_session_iframe: `${SERVICE_LOGIN_URL}/openidsso.jsp`,
end_session_endpoint: `${SERVICE_LOGIN_URL}/logout?continue=${OAUTH_URL}/authorize?client_id=${config.clientId}%26response_type=token%26redirect_uri=${config.redirectUri}%26scope=${config.scope}`,
introspect_endpoint: `${OAUTH_URL}/tokeninfo`,
token_endpoint: `${OAUTH_URL}/token`,
userinfo_endpoint: `${USERS_URL}/api/usuarios/@me`,
};
Exemplo em playground/src/auth/oauth-provider.js
Além do provedor também é preciso configurar o cliente, que representa a aplicação que irá consumir os recursos autenticados.
ℹ️ Atualmente as aplicações clientes são registradas e mantidas pela equipe da Plataforma, caso não possua os dados necessários, favor entrar em contato.
const clientConfig = {
scope: "SCOPES",
clientId: "CLIENT_ID",
redirectUri: `${window.location.origin}/auth/callback.html`,
silentRedirectUri: `${window.location.origin}/auth/silent-callback.html`,
};
Onde:
scope e clientId são obtidos ao registrar uma aplicaçãoredirectUri e silentRedirectUri são os endereços de redirecionamento, que serão abordados mais a frente, nas seções Lidando com redirecionamento do Login e Obtendo novo token de acesso em segundo plano, respectivamente.Exemplo em playground/src/auth/oauth-application.js
As configurações do cliente e do provedor OAuth podem ser mescladas e utilizadas para criar uma instância do OAuth para a aplicação.
É por meio desta instância que serão realizadas as interações com recursos de autenticação como: login, logout, obter token de acesso, verificar se há sessão ativa, obter dados do usuário, etc.
const OAUTH_URL = window["___bth"].envs.suite.oauth.v1.host;
const SERVICE_LOGIN_URL = window["___bth"].envs.suite["service-login"].v1.host;
const USERS_URL = window["___bth"].envs.suite.usuarios.v1.host;
const oAuthConfig = {
scope: "SCOPES",
clientId: "CLIENT_ID",
redirectUri: `${window.location.origin}/auth/callback.html`,
silentRedirectUri: `${window.location.origin}/auth/silent-callback.html`,
provider: {
authorization_endpoint: `${OAUTH_URL}/authorize`,
check_session_iframe: `${SERVICE_LOGIN_URL}/openidsso.jsp`,
end_session_endpoint: `${SERVICE_LOGIN_URL}/logout?continue=${OAUTH_URL}/authorize?client_id=${config.clientId}%26response_type=token%26redirect_uri=${config.redirectUri}%26scope=${config.scope}`,
introspect_endpoint: `${OAUTH_URL}/tokeninfo`,
token_endpoint: `${OAUTH_URL}/token`,
userinfo_endpoint: `${USERS_URL}/api/usuarios/@me`,
},
};
export const oAuthApp: OAuthApplication = new OAuthApplication(oAuthConfig);
Exemplo em playground/src/auth/oauth-application.js
Quando a aplicação for inicializada e não houver uma sessão ativa, deve-se chamar o método login(), disponível na instância do OAuth. Este método irá lidar com os passos necessários para o usuário efetuar o login.
import { oAuthApp } from "./oauth-application.ts";
if (!oAuthApp.hasActiveSession()) {
oAuthApp.login();
} else {
/**
* Iniciar a aplicação, renderizando recursos autenticados
*/
bootstrap();
}
Exemplo em playground/src/index.js
Após o usuário efetuar o login, ele será redirecionado para o redirectUri configurado na instância do OAuth.
Este redirecionamento irá entregar para a aplicação alguns valores por meio de parâmetros da URL. Nesta página a aplicação deverá executar o método handleCallback() da instância do OAuth para prosseguir com o fluxo.
import { oAuthApp } from "./oauth-application.ts";
oAuthApp.handleCallback();
Exemplo em playground/src/auth/callback/callback.html e playground/src/auth/callback/callback.js
Após autenticado, é possível renovar o token de acesso em segundo plano. Para isso é necessário ter uma página que emita algumas informações para a aplicação de origem. Essa página é configurada no silentRedirectUri ao instanciar a aplicação OAuth.
<html>
<head>
<script>
parent.postMessage(location.hash, location.origin);
</script>
</head>
</html>
Exemplo em playground/src/auth/callback/silent-callback.html
Durante o ciclo de vida de uma sessão alguns eventos provenientes de autenticação podem ser capturados para apresentar um feedback ao usuário.
A captura dos eventos pode ser feita por meio da instância de um Monitor de Eventos, criado a partir da instância do OAuth.
import { OAuthMonitor } from "@betha-plataforma/oauth";
import { oauthApp } from "./oauth-application.ts";
const monitorOptions = {
app: oauthApp,
interval: 1000,
};
const monitor = new OAuthMonitor(monitorOptions, {
onSessionChanged: () => {
// OAuth session changed
},
onSessionEnded: () => {
// OAuth session ended
},
onSessionRestablished: () => {
// OAuth session restabilished
},
});
monitor.start();
Exemplo em playground/src/auth/oauth-monitor.js
O componente recomendado para o feedback é a modal. Ela deve ser utilizada no formato bloqueante, ou seja, não permite fechar por meio da interface ou do teclado. Isto evita que o usuário utilize o sistema que pode estar inoperável por falta de autenticação. Para evitar empilhamento, é sugerido fechar as modais abertas ao receber qualquer evento.
As interfaces apresentadas são modelos baseados no Design System dos sistemas Cloud da Betha. É importante preservar as características do sistema no qual as interfaces serão apresentadas.
ℹ️ Nos exemplos abaixo foi utilizado o Bootstrap 4, que é um framework bem comum para abstrair o comportamento e estilização dos componentes.
Abaixo um modelo de apresentação quando a sessão for alterada

<div
id="session_changed_modal"
class="modal fade"
data-backdrop="static"
data-keyboard="false"
tabindex="-1"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body p-4">
<div class="row text-center">
<div class="col-md-12">
<img
style="width: 110px; margin-bottom: 12px;"
src="/images/user-changed.png"
/>
<h5 class="mb-4">
O usuário <strong id="session_changed_userid"></strong> entrou no
sistema
</h5>
</div>
</div>
<div class="text-center">
<button
class="btn btn-primary"
onclick="window.location.reload()"
style="width: 220px;"
>
Atualizar página
</button>
</div>
</div>
</div>
</div>
</div>
O evento pode ser capturado por meio do método onSessionChanged, disponível no Monitor de Eventos
new OAuthMonitor(monitorOptions, {
onSessionChanged: () => {
const userInfo = getUserInfo();
document.querySelector("#session_changed_userid").innerHTML = userInfo.id;
$("#session_ended_modal").modal("hide");
$("#session_changed_modal").modal("show");
},
});
Exemplo em playground/src/index.html e playground/src/auth/oauth-monitor.js
Abaixo um modelo de apresentação quando a sessão for encerrada

<div
id="session_ended_modal"
class="modal fade"
data-backdrop="static"
data-keyboard="false"
tabindex="-1"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body p-4">
<div class="row text-center">
<div class="col-md-12">
<img
style="width: 110px; margin-bottom: 12px;"
src="/images/logout.png"
/>
<h5 class="mb-4">Você saiu do sistema</h5>
</div>
</div>
<div class="text-center">
<button
class="btn btn-primary"
onclick="window.location.reload()"
style="width: 220px;"
>
Fazer login
</button>
</div>
</div>
</div>
</div>
</div>
O evento pode ser capturado por meio do método onSessionEnded, disponível no Monitor de Eventos
new OAuthMonitor(monitorOptions, {
onSessionEnded: () => {
$("#session_changed_modal").modal("hide");
$("#session_ended_modal").modal("show");
},
});
Exemplo em playground/src/index.html e playground/src/auth/oauth-monitor.js
Esta parte da interface compõe a estrutura visual da aplicação e deve ser utilizada para fornecer as informações da sessão atual como nome, usuário e foto e também possibilita a realização do logout e acesso à Central do Usuário.

ℹ️ Neste exemplo foi utilizado os Web Components da biblioteca @betha-plataforma/estrutura-componentes, que fornece os componentes necessários para compor a estrutura de uma aplicação front-end de maneira agnóstica a frameworks
Criando o componente na interface
<html>
<head>
<link
rel="stylesheet"
href="https://unpkg.com/@betha-plataforma/estrutura-componentes/dist/estrutura-componentes/estrutura-componentes.css"
/>
</head>
<body>
<bth-app menu-vertical>
<bth-conta-usuario slot="menu_ferramentas"></bth-conta-usuario>
</bth-app>
<script
type="module"
src="https://unpkg.com/@betha-plataforma/estrutura-componentes/dist/estrutura-componentes/estrutura-componentes.esm.js"
></script>
<script
nomodule
src="https://unpkg.com/@betha-plataforma/estrutura-componentes/dist/estrutura-componentes/estrutura-componentes.js"
></script>
</body>
</html>
Configurando informações da sessão e método de logout
import { oAuthApp } from "./oauth-application";
const profile = oAuthApp.getUser();
const accessToken = oAuthApp.getSession().accessToken.access_token;
const contaUsuario = document.querySelector("bth-conta-usuario");
contaUsuario.usuario = profile.id;
contaUsuario.nome = profile.name;
contaUsuario.fotoUrl = `${profile.photo}?access_token=${accessToken}`;
contaUsuario.addEventListener("logout", async () => {
await oAuthApp.logout();
});
Exemplo em playground/src/index.html, playground/src/app/bootstrap.js e playground/src/auth/services/authentication.js
Ao interagir com recursos autenticados por meio de requisições HTTP, algumas operações podem ser padronizadas no intuito de abstrair a necessidade de lidar com autenticação em cada funcionalidade. Geralmente cria-se um Client HTTP responsável por implementar este mecanismo.
ℹ️ Neste exemplo foi utilizado o Axios, que é um HTTP Client bem comum para Browsers.
O cabeçalho Authorization deve estar presente, com o valor Bearer <AUTH_TOKEN>, onde o AUTH_TOKEN pode ser obtido da instância do OAuth.
import { oAuthApp } from './oauth-application';
const Axios = axios.create();
/**
* Registra interceptor para autenticar requisições
*/
Axios.interceptors.request.use(config => {
if (config.method === 'OPTIONS') {
return config;
}
const accessToken = oAuthApp.getSession().accessToken.access_token;
config.headers.Authorization = `Bearer ${accessToken}`;
return config;
});
export Axios;
Exemplo em playground/src/core/api.js e playground/src/auth/services/authentication-context.js
As requisições que falharam com o código de resposta 401 (Unauthorized) devem ser armazenadas. Na sequência o processo de atualização do token da sessão deve ser realizado:
loginimport { oAuthApp } from "./oauth-application";
import { addRequestRetry } from "./retries.js";
const Axios = axios.create();
/**
* Registra interceptor para lidar com requisições que falharam
*/
Axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.response.status === 401) {
return new Promise((resolve, reject) =>
addRequestRetry(error.config, resolve, reject)
);
}
return Promise.reject(error);
}
);
export default Axios;
O interceptor armazena as requisições que falharam para possibilitar o mecanismo de retentativa. Caso não consiga atualizar o token, é solicitado o login.
import api from "./api";
import { oAuthApp } from "./oauth-application";
let isRefreshingToken = false;
let retryQueue = [];
async function requireAuthentication() {
try {
await oAuthApp.silentRefresh();
} catch (e) {
return oAuthApp.login();
}
const session = oAuthApp.getSession();
return session.accessToken.access_token;
}
export function addRequestRetry(request, resolve, reject) {
retryQueue.push({ request, resolve, reject });
if (!isRefreshingToken) {
startRefreshing();
return requireAuthentication()
.then(() => {
retryAllRequests();
stopRefreshing();
})
.catch(stopRefreshing);
}
}
function retryAllRequests() {
retryQueue.forEach(retryRequest);
retryQueue = [];
}
function retryRequest({ request, resolve, reject }) {
api.request(request).then(resolve).then(reject);
}
function startRefreshing() {
isRefreshingToken = true;
}
function stopRefreshing() {
isRefreshingToken = false;
}
Exemplo em playground/src/core/api.js, playground/src/core/retries.js e playground/src/auth/services/authentication.js
Exemplos podem ser encontrados no playground
FAQs
Biblioteca JavaScript para lidar com o fluxo do OAuth 2.0 em aplicações Web, com suporte a TypeScript.
The npm package @betha-plataforma/oauth receives a total of 24 weekly downloads. As such, @betha-plataforma/oauth popularity was classified as not popular.
We found that @betha-plataforma/oauth demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.