Need your help community
by Bill Burke
If you're using Keycloak and you like it, help us out by writing a blog
about it. Anything would be great. A few sentences saying that you're
uing keycloak and like it. Or, if you're ambitious, give us something
more detailed on how you are deploying keycloak in your company. I
would love to read some user stories just to get insight on how people
are using us! Let me know if anybody does this and I'll link you from
my blog.
Thanks,
Bill
--
Bill Burke
JBoss, a division of Red Hat
http://bill.burkecentral.com
10 years, 1 month
Re: [keycloak-user] Token refresh from Android native app
by Frank French
Thank you. That help resolved my problem.
Frank
Frensham House, Farnham Business Park, Weydon Lane, Farnham, Surrey, GU9
8QT
T: +44 (0)1628 552000 F: +44 (0)1628 552001
W: www.causeway.com
<outbind://11-000000002A37FEA00AFFD211855800A024AD4A260700B1F974DB383DD2118
54800A024AD4A260000000712B200002361683326FEAC42BECD9297DA5B98F0000000EDE5D8
0000/www.causeway.com>
Please consider the environment before printing this emai
On 03/11/2014 08:36, "Stian Thorgersen" <stian(a)redhat.com> wrote:
>Simple example of refreshing token (from our testsuite):
>
>https://github.com/keycloak/keycloak/blob/master/testsuite/integration/src
>/test/java/org/keycloak/testsuite/OAuthClient.java#L197
>
>
>
>----- Original Message -----
>> From: "Frank French" <Frank.French(a)causeway.com>
>> To: keycloak-user(a)lists.jboss.org
>> Sent: Sunday, 2 November, 2014 11:10:32 PM
>> Subject: [keycloak-user] Token refresh from Android native app
>>
>>
>> I¹m currently using the browser to initially log in to my app. After
>>the user
>> credentials have been validated I intercept the url containing the code
>>and
>> use that on the resource /realms/myrealm/tokens/access/codes. To
>>convert the
>> code into a token. I am then able to use the embedded access token for
>> subsequent API calls. This works fine until the access token expires.
>>
>> When the access token expires the service returns a 401. At present the
>>only
>> way I can get past this is to redirect the user back to the login page
>>and
>> start the above process again (creates another session). I would only
>>expect
>> to have to do this if the SSO idle timeout and been reached (it hasn¹t).
>> I¹ve looked through the API docs and found the resource
>> /realms/myrealm/token/refresh. I¹ve tried using this resource but have
>> failed miserably. Could someone please explain how to use this resource
>>or
>> point me somewhere else if I¹ve got it completely wrong.
>>
>> Example code below.
>>
>>
>>
>> private JWSToken refreshToken(String accessToken , String refreshToken )
>> throws IOException, InvalidTokenException{
>>
>> JWSToken token = null ;
>>
>>
>>
>> String url = " http://127.0.0.1:8080/auth/realms/myrealm/tokens/refresh
>>" ;
>>
>> //String query = ³refresh_token=³ + refreshToken ;
>>
>> //String query = ³refresh_token=³ + refreshToken + ³client_id=" +
>>CLIENT_ID "
>> ;
>>
>> String query = "refresh_token=" + refreshToken + "client_id=" +
>>CLIENT_ID +
>> "grant_type=refresh_token" ;
>>
>> //Tried all the above
>>
>>
>>
>>
>> HttpPost httppost = new HttpPost( url );
>>
>> httppost .addHeader( ³Authorization ² , ³Bearer ³ + accessToken );
>>//401 with
>> 400 without
>>
>> httppost .addHeader( "Content-Type" ,
>>"application/x-www-form-urlencoded" );
>>
>>
>>
>> ByteArrayEntity entity = new ByteArrayEntity( query .getBytes());
>>
>> httppost .setEntity( entity );
>>
>> HttpResponse response = httpclient .execute( httppost );
>>
>>
>>
>>
>> int status = response .getStatusLine().getStatusCode();
>>
>>
>>
>>
>> if ( status == HttpStatus. SC_OK ){
>>
>> InputStream stream = null ;
>>
>>
>>
>>
>> try {
>>
>> stream = response .getEntity().getContent();
>>
>>
>>
>>
>> ObjectMapper mapper = new ObjectMapper();
>>
>> token = mapper .readValue( stream , JWSToken. class );
>>
>> } finally {
>>
>> if ( stream != null ){
>>
>> stream .close();
>>
>> }
>>
>> }
>>
>> } else {
>>
>> throw new InvalidTokenException() ;
>>
>> }
>>
>>
>>
>> return token ;
>>
>> }
>>
>>
>>
>>
>>
>>
>>
>> Causeway is committed towards reducing its carbon footprint. Please
>>consider
>> the environment before printing this email.
>>
>>
>>
>>
>> Disclaimer Notice :-
>>
>> The message and any attachments contained in this e-mail are intended
>>for the
>> named recipient(s) only. It may contain privileged or confidential
>> information or information which is exempt from disclosure under the
>> applicable laws. If you are not the intended recipient(s), you must not
>> read, print, retain, copy distribute, forward or take any or refrain
>>from
>> taking any action in reliance on it or any of its attachments. If you
>>have
>> received or have been forwarded this e-mail in error, please notify us
>> immediately by return e-mail or telephone (+44 (0)1628 552000) and
>>delete
>> this message from the computer or any other data-reading device in its
>> entirety.
>>
>> Please advise us immediately if you do not or your employer does not
>>consent
>> to Internet e-mail for messages of this nature.
>>
>> Internet communications cannot be guaranteed to be secure and
>>error-free as
>> the information could be intercepted, corrupted, lost, arrive late or
>> contain viruses. The sender and this Company therefore do not and shall
>>not
>> accept any liability or responsibility of whatsoever nature in the
>>context
>> of this message and its attachment(s) which arises as a result of
>>Internet
>> transmission. Opinions, conclusion, representations, views and such
>>other
>> information in this message that do not relate to the official business
>>of
>> this Company shall be understood as neither given nor endorsed by it.
>>
>> Registered Office: Comino House, Furlong Road, Bourne End,
>>Buckinghamshire,
>> SL8 5AQ
>> Registered in England No: 3921897 www.causeway.com
>>
>> _______________________________________________
>> keycloak-user mailing list
>> keycloak-user(a)lists.jboss.org
>> https://lists.jboss.org/mailman/listinfo/keycloak-user
>
>_______________________________________________
>keycloak-user mailing list
>keycloak-user(a)lists.jboss.org
>https://lists.jboss.org/mailman/listinfo/keycloak-user
________________________________
Causeway is committed towards reducing its carbon footprint. Please consider the environment before printing this email.
________________________________
Disclaimer Notice :-
The message and any attachments contained in this e-mail are intended for the named recipient(s) only. It may contain privileged or confidential information or information which is exempt from disclosure under the applicable laws. If you are not the intended recipient(s), you must not read, print, retain, copy distribute, forward or take any or refrain from taking any action in reliance on it or any of its attachments. If you have received or have been forwarded this e-mail in error, please notify us immediately by return e-mail or telephone (+44 (0)1628 552000) and delete this message from the computer or any other data-reading device in its entirety.
Please advise us immediately if you do not or your employer does not consent to Internet e-mail for messages of this nature.
Internet communications cannot be guaranteed to be secure and error-free as the information could be intercepted, corrupted, lost, arrive late or contain viruses. The sender and this Company therefore do not and shall not accept any liability or responsibility of whatsoever nature in the context of this message and its attachment(s) which arises as a result of Internet transmission. Opinions, conclusion, representations, views and such other information in this message that do not relate to the official business of this Company shall be understood as neither given nor endorsed by it.
Registered Office: Comino House, Furlong Road, Bourne End, Buckinghamshire, SL8 5AQ
Registered in England No: 3921897 www.causeway.com
10 years, 1 month
Fwd: Java API documentation
by Alexander Chriztopher
---------- Forwarded message ----------
From: Alexander Chriztopher <alexander.chriztopher(a)gmail.com>
Date: Mon, Nov 3, 2014 at 9:44 AM
Subject: Re: [keycloak-user] Java API documentation
To: Stian Thorgersen <stian(a)redhat.com>
Hi Stian,
I have opened an issue here : https://issues.jboss.org/browse/KEYCLOAK-811
Thanks for your help.
On Mon, Nov 3, 2014 at 9:30 AM, Stian Thorgersen <stian(a)redhat.com> wrote:
>
>
> ----- Original Message -----
> > From: "Alexander Chriztopher" <alexander.chriztopher(a)gmail.com>
> > To: "Kamal Jagadevan" <j.kamal(a)ymail.com>
> > Cc: keycloak-user(a)lists.jboss.org
> > Sent: Friday, 31 October, 2014 4:59:24 PM
> > Subject: Re: [keycloak-user] Java API documentation
> >
> > I have upgraded to 1.0.4.Final but still have the issue.
> >
> > I would consider this as a bug in Keycloak -well in the client at least-
> as
> > am using Wildfly and i am embedding the Jackson dependencies with my war
> > anyway. These dependencies are pulled by the Keycloak ones.
> >
> > @Keycloak team : should i open an issue for this one ?
>
> Sure
>
> >
> > On Wed, Oct 29, 2014 at 8:16 PM, Kamal Jagadevan < j.kamal(a)ymail.com >
> wrote:
> >
> >
> >
> > Hi Alexander,
> > On a second look, my problem was with my tomcat application that
> integrates
> > with Keycloak. This tomcat application was using fasterxml jackson parser
> > whereas keycloak implementation uses codehaus jackson which gets
> overridden
> > during runtime. I was able to overcome this problem by creating
> > PropertyNamingStrategy and set it to ObjectMapper before deserializing
> the
> > JSON.
> >
> > Alternatively Keycloak implementation can be modified to use fasterxml
> > jackson databinding.
> >
> > -Kamal
> >
> >
> > From: Alexander Chriztopher < alexander.chriztopher(a)gmail.com >
> > To: Kamal Jagadevan < j.kamal(a)ymail.com >
> > Cc: " keycloak-user(a)lists.jboss.org " < keycloak-user(a)lists.jboss.org >
> > Sent: Monday, October 27, 2014 12:54 PM
> > Subject: Re: [keycloak-user] Java API documentation
> >
> > Hi Kamal and thanks.
> >
> > Am using the keycloak admin client which brings the following Jackson
> > dependency : jackson-core-asl:1.9.9 and can not override this. I also
> don't
> > have the option to change the property mapping as it comes with the
> Keycloak
> > distribution am using :-(
> >
> >
> >
> > On Mon, Oct 27, 2014 at 4:54 PM, Kamal Jagadevan < j.kamal(a)ymail.com >
> wrote:
> >
> >
> >
> > Hi Alexander,
> > I had faced the same problem few days back it is because of the mismatch
> > between JSONProperty and POJO variable name(getter method) that too with
> > fasterxml jackson parser.
> > If you use codehaus jackson parser you wouldnt get any problem. One work
> > around to this problem is to update the POJO variable name to reflect the
> > JSONProperty name.
> > Similar problem is observed in multiple places where deserialization
> kicks
> > in..
> >
> > Specifically it is because of this
> >
> > @JsonProperty(" access_token ")
> > protected String token ;
> >
> > Hi Bill,
> > Do you have any other ideas besides updating POJOs member variable name
> > matching the JSON property? Please advise.
> >
> > Thanks
> > Kamal
> >
> >
> >
> > From: Alexander Chriztopher < alexander.chriztopher(a)gmail.com >
> > To: " keycloak-user(a)lists.jboss.org " < keycloak-user(a)lists.jboss.org >
> > Sent: Monday, October 27, 2014 11:45 AM
> > Subject: [keycloak-user] Java API documentation
> >
> > Hi All,
> > Am using Keycloak 1.0.2.Final and am getting this error when using the
> rest
> > API :
> > Caused by:
> com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException :
> > Unrecognized field "access_token" (class
> > org.keycloak.representations.AccessTokenResponse), not marked as
> ignorable
> > (7 known properties: "tokenType", "notBeforePolicy", "token",
> "expiresIn",
> > "sessionState", "refreshToken", "idToken"])
> > at [Source: org.apache.http.conn.EofSensorInputStream@11b8a95d; line: 1,
> > column: 18] (through reference chain:
> > org.keycloak.representations.AccessTokenResponse["access_token"])
> > at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(
> > UnrecognizedPropertyException.java:51 )
> [jackson-databind-2.3.2.jar:2.3.2]
> > at
> >
> com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(
> > DeserializationContext.java:671 ) [jackson-databind-2.3.2.jar:2.3.2]
> > at
> >
> com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(
> > StdDeserializer.java:771 ) [jackson-databind-2.3.2.jar:2.3.2]
> > at
> >
> com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(
> > BeanDeserializerBase.java:1297 ) [jackson-databind-2.3.2.jar:2.3.2]
> > at
> >
> com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(
> > BeanDeserializerBase.java:1275 ) [jackson-databind-2.3.2.jar:2.3.2]
> > at
> com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(
> > BeanDeserializer.java:247 ) [jackson-databind-2.3.2.jar:2.3.2]
> > at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(
> > BeanDeserializer.java:118 ) [jackson-databind-2.3.2.jar:2.3.2]
> > at com.fasterxml.jackson.databind.ObjectReader._bind(
> ObjectReader.java:1233
> > ) [jackson-databind-2.3.2.jar:2.3.2]
> > at com.fasterxml.jackson.databind.ObjectReader.readValue(
> > ObjectReader.java:677 ) [jackson-databind-2.3.2.jar:2.3.2]
> > at
> >
> org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.readFrom(
> > ResteasyJackson2Provider.java:120 )
> > [resteasy-jackson2-provider-3.0.8.Final.jar:]
> > at
> >
> org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.readFrom(
> > AbstractReaderInterceptorContext.java:59 )
> [resteasy-jaxrs-3.0.8.Final.jar:]
> > at
> >
> org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(
> > AbstractReaderInterceptorContext.java:51 )
> [resteasy-jaxrs-3.0.8.Final.jar:]
> > at
> >
> org.jboss.resteasy.security.doseta.DigitalVerificationInterceptor.aroundReadFrom(
> > DigitalVerificationInterceptor.java:32 )
> [resteasy-crypto-3.0.8.Final.jar:]
> > at
> >
> org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(
> > AbstractReaderInterceptorContext.java:53 )
> [resteasy-jaxrs-3.0.8.Final.jar:]
> > at
> >
> org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.aroundReadFrom(
> > GZIPDecodingInterceptor.java:59 ) [resteasy-jaxrs-3.0.8.Final.jar:]
> > at
> >
> org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(
> > AbstractReaderInterceptorContext.java:53 )
> [resteasy-jaxrs-3.0.8.Final.jar:]
> > at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(
> > ClientResponse.java:248 ) [resteasy-client-3.0.8.Final.jar:]
> > ... 164 more
> >
> > Was wondering where this comes from as am using the 1.0.2.Final admin
> api and
> > have updated my Wildfly Server accordingly.
> >
> > _______________________________________________
> > keycloak-user mailing list
> > keycloak-user(a)lists.jboss.org
> > https://lists.jboss.org/mailman/listinfo/keycloak-user
> >
> >
> >
> >
> >
> >
> > _______________________________________________
> > keycloak-user mailing list
> > keycloak-user(a)lists.jboss.org
> > https://lists.jboss.org/mailman/listinfo/keycloak-user
>
10 years, 1 month
Token refresh from Android native app
by Frank French
I'm currently using the browser to initially log in to my app. After the user credentials have been validated I intercept the url containing the code and use that on the resource /realms/myrealm/tokens/access/codes. To convert the code into a token. I am then able to use the embedded access token for subsequent API calls. This works fine until the access token expires.
When the access token expires the service returns a 401. At present the only way I can get past this is to redirect the user back to the login page and start the above process again (creates another session). I would only expect to have to do this if the SSO idle timeout and been reached (it hasn't). I've looked through the API docs and found the resource /realms/myrealm/token/refresh. I've tried using this resource but have failed miserably. Could someone please explain how to use this resource or point me somewhere else if I've got it completely wrong.
Example code below.
private JWSToken refreshToken(String accessToken, String refreshToken) throws IOException, InvalidTokenException{
JWSToken token = null;
String url = "http://127.0.0.1:8080/auth/realms/myrealm/tokens/refresh";
//String query = "refresh_token=" + refreshToken;
//String query = "refresh_token=" + refreshToken + "client_id=" + CLIENT_ID";
String query = "refresh_token=" + refreshToken + "client_id=" + CLIENT_ID + "grant_type=refresh_token";
//Tried all the above
HttpPost httppost = new HttpPost(url);
httppost.addHeader("Authorization", "Bearer " + accessToken); //401 with 400 without
httppost.addHeader("Content-Type", "application/x-www-form-urlencoded");
ByteArrayEntity entity = new ByteArrayEntity(query.getBytes());
httppost.setEntity(entity);
HttpResponse response = httpclient.execute(httppost);
int status = response.getStatusLine().getStatusCode();
if (status == HttpStatus.SC_OK){
InputStream stream = null;
try{
stream = response.getEntity().getContent();
ObjectMapper mapper = new ObjectMapper();
token = mapper.readValue(stream, JWSToken.class);
}finally{
if (stream != null){
stream.close();
}
}
}else{
throw new InvalidTokenException();
}
return token;
}
________________________________
Causeway is committed towards reducing its carbon footprint. Please consider the environment before printing this email.
________________________________
Disclaimer Notice :-
The message and any attachments contained in this e-mail are intended for the named recipient(s) only. It may contain privileged or confidential information or information which is exempt from disclosure under the applicable laws. If you are not the intended recipient(s), you must not read, print, retain, copy distribute, forward or take any or refrain from taking any action in reliance on it or any of its attachments. If you have received or have been forwarded this e-mail in error, please notify us immediately by return e-mail or telephone (+44 (0)1628 552000) and delete this message from the computer or any other data-reading device in its entirety.
Please advise us immediately if you do not or your employer does not consent to Internet e-mail for messages of this nature.
Internet communications cannot be guaranteed to be secure and error-free as the information could be intercepted, corrupted, lost, arrive late or contain viruses. The sender and this Company therefore do not and shall not accept any liability or responsibility of whatsoever nature in the context of this message and its attachment(s) which arises as a result of Internet transmission. Opinions, conclusion, representations, views and such other information in this message that do not relate to the official business of this Company shall be understood as neither given nor endorsed by it.
Registered Office: Comino House, Furlong Road, Bourne End, Buckinghamshire, SL8 5AQ
Registered in England No: 3921897 www.causeway.com
10 years, 1 month
Java API documentation
by Alexander Chriztopher
Hi All,
Am using Keycloak 1.0.2.Final and am getting this error when using the rest
API :
Caused by:
*com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException*:
Unrecognized field "access_token" (class
org.keycloak.representations.AccessTokenResponse), not marked as ignorable
(7 known properties: "tokenType", "notBeforePolicy", "token", "expiresIn",
"sessionState", "refreshToken", "idToken"])
at [Source: org.apache.http.conn.EofSensorInputStream@11b8a95d; line: 1,
column: 18] (through reference chain:
org.keycloak.representations.AccessTokenResponse["access_token"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(
*UnrecognizedPropertyException.java:51*) [jackson-databind-2.3.2.jar:2.3.2]
at
com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(
*DeserializationContext.java:671*) [jackson-databind-2.3.2.jar:2.3.2]
at
com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(
*StdDeserializer.java:771*) [jackson-databind-2.3.2.jar:2.3.2]
at
com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(
*BeanDeserializerBase.java:1297*) [jackson-databind-2.3.2.jar:2.3.2]
at
com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(
*BeanDeserializerBase.java:1275*) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(
*BeanDeserializer.java:247*) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(
*BeanDeserializer.java:118*) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ObjectReader._bind(
*ObjectReader.java:1233*) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ObjectReader.readValue(
*ObjectReader.java:677*) [jackson-databind-2.3.2.jar:2.3.2]
at
org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.readFrom(
*ResteasyJackson2Provider.java:120*)
[resteasy-jackson2-provider-3.0.8.Final.jar:]
at
org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.readFrom(
*AbstractReaderInterceptorContext.java:59*)
[resteasy-jaxrs-3.0.8.Final.jar:]
at
org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(
*AbstractReaderInterceptorContext.java:51*)
[resteasy-jaxrs-3.0.8.Final.jar:]
at
org.jboss.resteasy.security.doseta.DigitalVerificationInterceptor.aroundReadFrom(
*DigitalVerificationInterceptor.java:32*) [resteasy-crypto-3.0.8.Final.jar:]
at
org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(
*AbstractReaderInterceptorContext.java:53*)
[resteasy-jaxrs-3.0.8.Final.jar:]
at
org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.aroundReadFrom(
*GZIPDecodingInterceptor.java:59*) [resteasy-jaxrs-3.0.8.Final.jar:]
at
org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(
*AbstractReaderInterceptorContext.java:53*)
[resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(
*ClientResponse.java:248*) [resteasy-client-3.0.8.Final.jar:]
... 164 more
Was wondering where this comes from as am using the 1.0.2.Final admin api
and have updated my Wildfly Server accordingly.
10 years, 1 month
Getting the current user name in EJB
by Alexander Chriztopher
Hi All,
Am trying to get the name and surname of the currently connected user by
doing this :
import java.io.Serializable;
import java.security.Principal;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJBContext;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import org.jboss.ejb3.annotation.SecurityDomain;
@Stateless(name="myEJB")
@LocalBean
@SecurityDomain("keycloak")
public class MyEJB implements Serializable {
private static final long serialVersionUID = 1L;
@Resource
private EJBContext ejbContext;
@RolesAllowed("ADMIN")
public void test() {
Principal principal = ejbContext.getCallerPrincipal();
System.out.println("principal.getName() = " + principal.getName());
}
}
This works nicely as i get a 403 if my currently connected user does have
the role : ADMIN.
My question is : does keycloak propagate the username or any other
information that would help me get the first name and last name of the
currently connected user ? Unfortunately, principal.getName() returns a
string like this : edd42240-85bf-4724-8d79-5374338506b7 which i don't know
the interpretation !
Thanks for any help.
10 years, 1 month