diff --git a/sso-imgs/sso-1.png b/sso-imgs/sso-1.png
new file mode 100644
index 0000000..6271ad7
Binary files /dev/null and b/sso-imgs/sso-1.png differ
diff --git a/sso-imgs/sso-10.png b/sso-imgs/sso-10.png
new file mode 100644
index 0000000..6199df3
Binary files /dev/null and b/sso-imgs/sso-10.png differ
diff --git a/sso-imgs/sso-11.png b/sso-imgs/sso-11.png
new file mode 100644
index 0000000..6293084
Binary files /dev/null and b/sso-imgs/sso-11.png differ
diff --git a/sso-imgs/sso-12.png b/sso-imgs/sso-12.png
new file mode 100644
index 0000000..5ab8ee4
Binary files /dev/null and b/sso-imgs/sso-12.png differ
diff --git a/sso-imgs/sso-13.png b/sso-imgs/sso-13.png
new file mode 100644
index 0000000..52e9179
Binary files /dev/null and b/sso-imgs/sso-13.png differ
diff --git a/sso-imgs/sso-14.png b/sso-imgs/sso-14.png
new file mode 100644
index 0000000..dc60b6d
Binary files /dev/null and b/sso-imgs/sso-14.png differ
diff --git a/sso-imgs/sso-2.png b/sso-imgs/sso-2.png
new file mode 100644
index 0000000..57c80fa
Binary files /dev/null and b/sso-imgs/sso-2.png differ
diff --git a/sso-imgs/sso-3.png b/sso-imgs/sso-3.png
new file mode 100644
index 0000000..280a96b
Binary files /dev/null and b/sso-imgs/sso-3.png differ
diff --git a/sso-imgs/sso-4.png b/sso-imgs/sso-4.png
new file mode 100644
index 0000000..43ff025
Binary files /dev/null and b/sso-imgs/sso-4.png differ
diff --git a/sso-imgs/sso-5.png b/sso-imgs/sso-5.png
new file mode 100644
index 0000000..daa8b75
Binary files /dev/null and b/sso-imgs/sso-5.png differ
diff --git a/sso-imgs/sso-6.png b/sso-imgs/sso-6.png
new file mode 100644
index 0000000..97f5b39
Binary files /dev/null and b/sso-imgs/sso-6.png differ
diff --git a/sso-imgs/sso-7.png b/sso-imgs/sso-7.png
new file mode 100644
index 0000000..2cae680
Binary files /dev/null and b/sso-imgs/sso-7.png differ
diff --git a/sso-imgs/sso-8.png b/sso-imgs/sso-8.png
new file mode 100644
index 0000000..796f704
Binary files /dev/null and b/sso-imgs/sso-8.png differ
diff --git a/sso-imgs/sso-9.png b/sso-imgs/sso-9.png
new file mode 100644
index 0000000..9f46a86
Binary files /dev/null and b/sso-imgs/sso-9.png differ
diff --git a/sso.md b/sso.md
new file mode 100644
index 0000000..c9e143d
--- /dev/null
+++ b/sso.md
@@ -0,0 +1,421 @@
+# From Monolith to K8s - Workshop
+
+
+## Installing and Configuring Keycloak
+During this step-by-step you will be using **Kubernetes Cluster** and a Keycloak as SSO to secure our API Gateway and Microservices.
+
+### Creating a Kubernetes Cluster with KIND
+
+```
+$ kind create cluster --name keycloak
+```
+
+Don't forget to set current cluster/context
+
+```
+$ kubectl cluster-info --context kind-keycloak
+```
+
+In this example we'll not create a namespace (It is not a best practice )
+
+### Adding Keycloak on Cluster Kubernetes
+
+```
+kubectl cretate -f https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/latest/kubernetes-examples/keycloak.yaml
+```
+
+Let's see the keycloak pod
+
+```
+$ kubectl get pods
+```
+
+## OAuth2 Concepts
+
+Before you start, I want to speak a bit about OAuth2 concepts:
+
+### OAuth2 Roles
+
+OAuth2 defines four roles:
+
+- Resource Owner
+
+ It is an user that have some resource protected, like your facebook account, Github repositories, Instagram images etc.
+
+ **In our case a Resource Owner is the admin user that approves or rejects a proposal**
+
+- Resource Server
+
+ Provides a protected resource and permits the access through valid access tokens. Examples of Resource Server: Facebook, Github, Instagram etc.
+
+ **In our case a Resource Server will be C4P Service, Email Service and Agenda Service**
+
+- Authorization Server
+
+ Authorization Server provides an API used to autenticates an user and generates an **access token**. Examples of Authorizaton Server: Spring Authorization Server, Okta, Google Identity etc.
+
+ **In our case, Keycloak is our Authorization Server**
+
+- Client
+
+ It is an application, web, SPA, Desktop or mobile, that want to access the resources from **Resource Owner**. A Client needs to be registred on Authorization Server, being identified by a **client id** and a **client secret**.
+
+ **In our case a Client is the API Gateway**
+
+
+
+### OAuth2 Grant Types:
+
+A Grant Type is a way that a Client use to give an **access token**, OAuth2 is very flexible and provides four grant types for us:
+
+- Password
+
+ Used when there is a strong relationship from Client and Authorization Server. The user provides your credentials (username and password) directly to Client, that forward (with his client id and client secret) the user's credentials to Authorization Server.
+
+- Client Credentials
+
+ Used when there is not an user involved, it is used when there is a sytem called a protected system, just client's credentials are provides to Authorization Server.
+
+- Authorization Code
+
+ Used when third applications want to access data from a protected resource without the Client see the user's credentials. By example:
+ When an user (Resource Owner) permits that the Travis CI (Client) access your repositories from Github (Authorization Server and Resource Owner).
+
+- Implicit
+
+ Is mostly used on Single Page Applications and Mobile Apps.
+ The user is redirected to Authorization Server's login page, but the redirect is made directly to user-agent (A browser, by example) with access token. With this way, the SPA or Mobile application knows directly the access token.
+
+
+It is a brief explanation about OAuth2, to more details [look here](https://oauth.net/2/)
+
+In this workshop we'll use **Authorization Code Grant Types**
+
+**Authorization Server:** *Keycloak*
+
+**Client:** *API Gateway*
+
+**Resourse Owner:** *Admin*
+
+**Resource Server:** *C4P Service, Agenda Service, Email Service*
+
+
+## Configuring Keycloak
+### 1 - Let's access Administration Console:
+
+
+
+### 2 - We'll access using our credentials passed through configurations
+
+
+
+### 3 - Let's create our realm (fmtok8s)
+
+
+
+ What is Keycloak's Realm?
+ A realm manages a set of users, credentials, roles, and groups. In our example, we'll create a Realm for fmtok8s application. A user belongs nd accesses one realm, a realm are isolated from one another, then if you create an user in Realm A the another Realm (B) cannot see, and if you create an user on Realm A this user cannot access Realm B.
+
+
+
+
+
+
+
+### 4 - Creating a Realm's Client
+
+
+
+What is Keycloak's Client?
+"Clients are applications and services that want to use Keycloak to secure themselves and provide a single sign-on solution". In our solution, we'll just one client API Gateway.
+
+
+
+
+
+
+
+### 5 - Configuring a Client
+
+The client configuration's page is very large, then I will divide it in two parts:
+
+Parte 1:
+
+
+Parte 2:
+
+
+## 6 - Creating an Realm's User
+
+
+
+
+
+After, you should set the user's password
+
+
+
+### 7 - Creating a Realm's Role
+
+
+
+
+
+### 8 - Adding a role to user
+
+
+
+
+## Changing API Gateway to secure our hidden microservices
+
+[API Gateway](https://github.com/mcruzdev/fmtok8s-api-gateway) was created with Spring Cloud Gateway. The Spring Cloud Gateway uses Spring Webflux working with reactive stack.
+
+There is a great lib called
+`org.keycloak:keycloak-spring-boot-starter` that help us to configure our application using keycloak and it runs better with Servlet applications. [See](https://keycloak.discourse.group/t/webflux-support-for-spring-boot-and-spring-security-adapters/2936)
+
+In this workshop, you will use Spring Security OAuth2. Let's go to use it.
+
+### Adding Spring OAuth2 dependecies in API Gateway
+
+```
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
+ org.springframework.cloud
+ spring-cloud-starter-security
+
+```
+
+### Configuring
+
+We should change the configuration of **API Gateway** on application.yml
+
+```
+spring:
+ security:
+ oauth2:
+ client:
+ provider:
+ oidc:
+ issuer-uri: http://localhost:8081/auth/realms/fmtok8s
+ registration:
+ oidc:
+ client-name: keycloak
+ provider: oidc
+ client-id: gateway
+ client-secret: 7208a758-e57c-4045-8c4a-9831bb2b8144
+```
+
+The property client-secret should be filled with client-secret from keycloak, let's get our client-secret from gateway keycloak client:
+
+**Copy gateway's secret:**
+
+
+
+
+And add a filter a new filter on **API Gateway** to relay de Token to hiden services
+
+```
+ cloud:
+ gateway:
+ default-filters:
+ - TokenRelay=
+ - RemoveRequestHeader=Cookie
+ httpclient:
+```
+
+### Creating class SecurityConfig to our Gateway
+
+```
+package com.salaboy.conferences.site.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
+import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@EnableWebFluxSecurity
+public class SecurityConfig {
+
+ @Bean
+ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
+ return http.csrf().disable()
+ .authorizeExchange()
+ .pathMatchers("/backoffice/**").hasRole("approver")
+ .anyExchange().permitAll()
+ .and()
+ .oauth2Login()
+ .and()
+ .oauth2Client()
+ .and()
+ .build();
+ }
+
+ @Bean
+ public ReactiveOAuth2UserService oidcUserService() {
+ final OidcReactiveOAuth2UserService delegate = new OidcReactiveOAuth2UserService();
+
+ return (userRequest) -> {
+ // Delegate to the default implementation for loading a user
+ return delegate.loadUser(userRequest).map(user -> {
+ Set mappedAuthorities = new HashSet<>();
+
+ user.getAuthorities().forEach(authority -> {
+ if (authority instanceof OidcUserAuthority) {
+ OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
+ mappedAuthorities.addAll(extractAuthorityFromClaims(oidcUserAuthority.getUserInfo().getClaims()));
+ }
+ });
+
+ return new DefaultOidcUser(mappedAuthorities, user.getIdToken(), user.getUserInfo());
+ });
+ };
+ }
+
+ public static List extractAuthorityFromClaims(Map claims) {
+ return mapRolesToGrantedAuthorities(getRolesFromClaims(claims));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Collection getRolesFromClaims(Map claims) {
+ return (Collection) claims.getOrDefault("groups",
+ claims.getOrDefault("roles", new ArrayList<>()));
+ }
+
+ private static List mapRolesToGrantedAuthorities(Collection roles) {
+ return roles.stream()
+ .map("ROLE_"::concat)
+ .map(SimpleGrantedAuthority::new)
+ .collect(Collectors.toList());
+ }
+}
+```
+
+
+## Securing our microservices (C4P)
+
+### We need to add some dependecies on pom.xml
+
+```
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.security
+ spring-security-oauth2-resource-server
+
+
+
+ org.springframework.security
+ spring-security-oauth2-jose
+
+```
+
+### Configuring Spring OAuth2 Resource Server through application.properties:
+
+```
+spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/fmtok8s
+```
+
+### Creat an class to configure CORS
+
+```
+package com.salaboy.conferences.c4p.rest.configuration;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.config.CorsRegistry;
+import org.springframework.web.reactive.config.WebFluxConfigurer;
+
+@Configuration
+public class CORSConfig implements WebFluxConfigurer {
+
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**").allowCredentials(true).allowedMethods("*");
+ }
+}
+```
+
+### Creating our SecurityConfig
+
+```
+package com.salaboy.conferences.c4p.rest.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import reactor.core.publisher.Mono;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@EnableWebFluxSecurity
+public class SecurityConfig {
+
+ @Bean
+ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
+
+ http
+ .csrf().disable()
+ .authorizeExchange(exchanges ->
+ exchanges
+ .pathMatchers(HttpMethod.POST, "/**").hasAnyAuthority("approver")
+ .pathMatchers(HttpMethod.DELETE, "/**").hasAnyAuthority("approver")
+ .anyExchange().permitAll())
+ .oauth2ResourceServer(oauth2 ->
+ oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(grantedAuthoritiesExtractor())));
+
+ return http.build();
+ }
+
+ Converter> grantedAuthoritiesExtractor() {
+ JwtAuthenticationConverter jwtAuthenticationConverter =
+ new JwtAuthenticationConverter();
+
+ jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new GrantedAuthoritiesExtractor());
+
+ return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
+ }
+
+ static class GrantedAuthoritiesExtractor implements Converter> {
+
+ @Override
+ public Collection convert(Jwt jwt) {
+
+ @SuppressWarnings("unchecked")
+ var roles = (List) jwt.getClaims().getOrDefault("groups", Collections.emptyList());
+
+ return roles.stream()
+ .map(SimpleGrantedAuthority::new)
+ .collect(Collectors.toList());
+ }
+ }
+}
+
+```
\ No newline at end of file